Merge pull request #3185 from FinnStutzenstein/export-dialog

Export dialog for motions
This commit is contained in:
Emanuel Schütze 2017-04-07 15:09:15 +02:00 committed by GitHub
commit 598dc30486
12 changed files with 517 additions and 243 deletions

View File

@ -4,9 +4,11 @@
https://openslides.org/ https://openslides.org/
Version 2.1.2 (unreleased) Version 2.2 (unreleased)
========================== ==========================
Motions:
- New export dialog.
Version 2.1.1 (2017-04-05) Version 2.1.1 (2017-04-05)
========================== ==========================

View File

@ -1346,6 +1346,10 @@ img {
padding: 0; padding: 0;
} }
.btn-group > label > input[type=radio] {
display: none;
}
/* user details */ /* user details */
.user_details fieldset { .user_details fieldset {
margin-bottom: 10px; margin-bottom: 10px;

View File

@ -648,6 +648,10 @@ angular.module('OpenSlidesApp.core.site', [
templateUrl: 'static/templates/core/checkbox.html', templateUrl: 'static/templates/core/checkbox.html',
overwriteOk: true, overwriteOk: true,
}); });
formlyConfig.setType({
name: 'select-radio',
templateUrl: 'static/templates/core/select-radio.html',
});
formlyConfig.setType({ formlyConfig.setType({
name: 'select-single', name: 'select-single',
extends: 'select', extends: 'select',

View File

@ -0,0 +1,17 @@
<div class="form-group">
<div>
<label class="control-label" ng-if="to.label">
{{ to.label }}
</label>
</div>
<div class="btn-group">
<label ng-repeat="option in to.options" class="btn btn-default btn-sm"
ng-class="{active: (model[options.key] === option.value)}"
ng-disabled="option.disabled">
<input type="radio" name="select-radio-{{ options.key }}" ng-value="option.value"
ng-model="model[options.key]" ng-checked="model[options.key] === option.value"
ng-disabled="option.disabled" ng-change="to.change(option.value)">
{{ option.name | translate }}
</label>
</div>
</div>

View File

@ -6,8 +6,10 @@ angular.module('OpenSlidesApp.motions.csv', [])
.factory('MotionCsvExport', [ .factory('MotionCsvExport', [
'gettextCatalog', 'gettextCatalog',
'Config',
'CsvDownload', 'CsvDownload',
function (gettextCatalog, CsvDownload) { 'lineNumberingService',
function (gettextCatalog, Config, CsvDownload, lineNumberingService) {
var makeHeaderline = function () { var makeHeaderline = function () {
var headerline = ['Identifier', 'Title', 'Text', 'Reason', 'Submitter', 'Category', 'Origin']; var headerline = ['Identifier', 'Title', 'Text', 'Reason', 'Submitter', 'Category', 'Origin'];
return _.map(headerline, function (entry) { return _.map(headerline, function (entry) {
@ -15,16 +17,36 @@ angular.module('OpenSlidesApp.motions.csv', [])
}); });
}; };
return { 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 = [ var csvRows = [
makeHeaderline() makeHeaderline()
]; ];
_.forEach(motions, function (motion) { _.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 = []; var row = [];
row.push('"' + motion.identifier !== null ? motion.identifier : '' + '"'); row.push('"' + motion.identifier !== null ? motion.identifier : '' + '"');
row.push('"' + motion.getTitle() + '"'); row.push('"' + motion.getTitle() + '"');
row.push('"' + motion.getText() + '"'); row.push('"' + text + '"');
row.push('"' + motion.getReason() + '"'); if (params.includeReason) {
row.push('"' + motion.getReason() + '"');
} else {
row.push('""');
}
var submitter = motion.submitters[0] ? motion.submitters[0].get_full_name() : ''; var submitter = motion.submitters[0] ? motion.submitters[0].get_full_name() : '';
row.push('"' + submitter + '"'); row.push('"' + submitter + '"');
var category = motion.category ? motion.category.name : ''; var category = motion.category ? motion.category.name : '';

View File

@ -8,9 +8,11 @@ angular.module('OpenSlidesApp.motions.docx', [])
'$http', '$http',
'$q', '$q',
'Config', 'Config',
'Category',
'gettextCatalog', 'gettextCatalog',
'FileSaver', 'FileSaver',
function ($http, $q, Config, gettextCatalog, FileSaver) { 'lineNumberingService',
function ($http, $q, Config, Category, gettextCatalog, FileSaver, lineNumberingService) {
var PAGEBREAK = '<w:p><w:r><w:br w:type="page" /></w:r></w:p>'; var PAGEBREAK = '<w:p><w:r><w:br w:type="page" /></w:r></w:p>';
var TAGS_NO_PARAM = ['b', 'strong', 'em', 'i']; var TAGS_NO_PARAM = ['b', 'strong', 'em', 'i'];
@ -19,10 +21,9 @@ angular.module('OpenSlidesApp.motions.docx', [])
var relationships; var relationships;
var contentTypes; var contentTypes;
// $scope.motionsFiltered, $scope.categories var getData = function (motions, params) {
var getData = function (motions, categories) {
var data = {}; var data = {};
var categories = Category.getAll();
// header // header
var headerline1 = [ var headerline1 = [
Config.translate(Config.get('general_event_name').value), Config.translate(Config.get('general_event_name').value),
@ -47,7 +48,7 @@ angular.module('OpenSlidesApp.motions.docx', [])
// motions // motions
data.tableofcontents_translation = gettextCatalog.getString('Table of contents'); data.tableofcontents_translation = gettextCatalog.getString('Table of contents');
data.motions = getMotionFullData(motions); data.motions = getMotionFullData(motions, params);
data.motions_list = getMotionShortData(motions); data.motions_list = getMotionShortData(motions);
data.no_motions = gettextCatalog.getString('No motions available.'); 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'), var translation = gettextCatalog.getString('Motion'),
sequential_translation = gettextCatalog.getString('Sequential number'), sequential_translation = gettextCatalog.getString('Sequential number'),
submitters_translation = gettextCatalog.getString('Submitters'), submitters_translation = gettextCatalog.getString('Submitters'),
status_translation = gettextCatalog.getString('Status'), status_translation = gettextCatalog.getString('Status'),
reason_translation = gettextCatalog.getString('Reason'), reason_translation = gettextCatalog.getString('Reason'),
data = _.map(motions, function (motion) { 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 { return {
motion_translation: translation, motion_translation: translation,
sequential_translation: sequential_translation, sequential_translation: sequential_translation,
@ -92,9 +98,9 @@ angular.module('OpenSlidesApp.motions.docx', [])
status_translation: status_translation, status_translation: status_translation,
status: motion.getStateName(), status: motion.getStateName(),
preamble: gettextCatalog.getString(Config.get('motions_preamble').value), preamble: gettextCatalog.getString(Config.get('motions_preamble').value),
text: html2docx(motion.getText()), text: html2docx(text),
reason_translation: motion.getReason().length === 0 ? '' : reason_translation, reason_translation: reason.length === 0 ? '' : reason_translation,
reason: html2docx(motion.getReason()), reason: html2docx(reason),
pagebreak: PAGEBREAK, pagebreak: PAGEBREAK,
}; };
}); });
@ -353,7 +359,19 @@ angular.module('OpenSlidesApp.motions.docx', [])
}; };
return { 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 = []; images = [];
relationships = []; relationships = [];
contentTypes = []; contentTypes = [];
@ -361,7 +379,7 @@ angular.module('OpenSlidesApp.motions.docx', [])
var content = window.atob(success.data); var content = window.atob(success.data);
var doc = new Docxgen(content); var doc = new Docxgen(content);
doc.setData(getData(motions, categories)); doc.setData(getData(motions, params));
doc.render(); doc.render();
var zip = doc.getZip(); var zip = doc.getZip();
@ -384,7 +402,7 @@ angular.module('OpenSlidesApp.motions.docx', [])
// wait for all images to be resolved // wait for all images to be resolved
$q.all(imgPromises).then(function () { $q.all(imgPromises).then(function () {
var out = zip.generate({type: 'blob'}); var out = zip.generate({type: 'blob'});
FileSaver.saveAs(out, 'motions-export.docx'); FileSaver.saveAs(out, params.filename);
}); });
}); });
}, },

View File

@ -4,60 +4,6 @@
angular.module('OpenSlidesApp.motions.motionservices', ['OpenSlidesApp.motions', 'OpenSlidesApp.motions.lineNumbering']) 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', [ .factory('MotionInlineEditing', [
'Editor', 'Editor',
'Motion', 'Motion',

View File

@ -5,24 +5,26 @@
angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf']) angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf'])
.factory('MotionContentProvider', [ .factory('MotionContentProvider', [
'operator',
'gettextCatalog', 'gettextCatalog',
'PDFLayout', 'PDFLayout',
'Category', 'Category',
'Config', 'Config',
'Motion', '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 * Provides the content as JS objects for Motions in pdfMake context
* @constructor * @constructor
*/ */
var createInstance = function(converter, motion, $scope) { var createInstance = function(converter, motion, motionVersion, changeRecommendationMode,
changeRecommendations, lineNumberMode, includeReason, includeComments) {
// title // title
var identifier = motion.identifier ? ' ' + motion.identifier : ''; var identifier = motion.identifier ? ' ' + motion.identifier : '';
var title = PDFLayout.createTitle( var title = PDFLayout.createTitle(
gettextCatalog.getString('Motion') + identifier + ': ' + gettextCatalog.getString('Motion') + identifier + ': ' +
motion.getTitle($scope.version) motion.getTitle(motionVersion)
); );
// subtitle // subtitle
@ -175,51 +177,49 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf'])
} }
// summary of change recommendations (for motion diff version only) // summary of change recommendations (for motion diff version only)
if ($scope.viewChangeRecommendations) { if (changeRecommendationMode == "diff") {
if ($scope.viewChangeRecommendations.mode == "diff") { var columnLineNumbers = [];
var columnLineNumbers = []; var columnChangeType = [];
var columnChangeType = []; angular.forEach(_.orderBy(changeRecommendations, ['line_from']), function(change) {
angular.forEach(_.orderBy($scope.change_recommendations, ['line_from']), function(change) { // line numbers column
// line numbers column var line;
var line; if (change.line_from >= change.line_to - 1) {
if (change.line_from >= change.line_to - 1) { line = change.line_from;
line = change.line_from; } else {
} else { line = change.line_from + ' - ' + (change.line_to - 1);
line = change.line_from + ' - ' + (change.line_to - 1); }
} columnLineNumbers.push(
columnLineNumbers.push( gettextCatalog.getString('Line') + ' ' + line + ': '
gettextCatalog.getString('Line') + ' ' + line + ': ' );
); // change type column
// change type column if (change.getType(motion.getVersion(motionVersion).text) === 0) {
if (change.getType(motion.getVersion($scope.version).text) === 0) { columnChangeType.push(gettextCatalog.getString("Replacement"));
columnChangeType.push(gettextCatalog.getString("Replacement")); } else if (change.getType(motion.getVersion(motionVersion).text) === 1) {
} else if (change.getType(motion.getVersion($scope.version).text) === 1) { columnChangeType.push(gettextCatalog.getString("Insertion"));
columnChangeType.push(gettextCatalog.getString("Insertion")); } else if (change.getType(motion.getVersion(motionVersion).text) === 2) {
} else if (change.getType(motion.getVersion($scope.version).text) === 2) { columnChangeType.push(gettextCatalog.getString("Deletion"));
columnChangeType.push(gettextCatalog.getString("Deletion")); }
} });
}); metaTableBody.push([
metaTableBody.push([ {
{ text: gettextCatalog.getString('Summary of change recommendations'),
text: gettextCatalog.getString('Summary of change recommendations'), style: ['bold', 'grey']
style: ['bold', 'grey'] },
}, {
{ columns: [
columns: [ {
{ text: columnLineNumbers.join('\n'),
text: columnLineNumbers.join('\n'), width: 'auto'
width: 'auto' },
}, {
{ text: columnChangeType.join('\n'),
text: columnChangeType.join('\n'), width: 'auto'
width: 'auto' }
} ],
], columnGap: 7,
columnGap: 7, style: 'grey'
style: 'grey' }
} ]);
]);
}
} }
// build table // build table
@ -241,7 +241,7 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf'])
// motion title // motion title
var motionTitle = function() { var motionTitle = function() {
return [{ return [{
text: motion.getTitle($scope.version), text: motion.getTitle(motionVersion),
style: 'heading3' style: 'heading3'
}]; }];
}; };
@ -257,28 +257,56 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf'])
// motion text (with line-numbers) // motion text (with line-numbers)
var motionText = function() { var motionText = function() {
var motionTextContent = motion.getTextByMode($scope.viewChangeRecommendations.mode, $scope.version); var motionTextContent = motion.getTextByMode(changeRecommendationMode, motionVersion);
return converter.convertHTML(motionTextContent, $scope.lineNumberMode); return converter.convertHTML(motionTextContent, lineNumberMode);
}; };
// motion reason heading // motion reason heading
var motionReason = function() { var motionReason = function() {
var reason = []; if (includeReason) {
if (motion.getReason($scope.version)) { var reason = [];
reason.push({ if (motion.getReason(motionVersion)) {
text: gettextCatalog.getString('Reason'), reason.push({
style: 'heading3', text: gettextCatalog.getString('Reason'),
marginTop: 25, style: 'heading3',
}); marginTop: 25,
reason.push(converter.convertHTML(motion.getReason($scope.version), $scope.lineNumberMode)); });
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 // getters
var getTitle = function() { var getTitle = function() {
return motion.getTitle($scope.version); return motion.getTitle(motionVersion);
}; };
var getIdentifier = function() { var getIdentifier = function() {
@ -299,8 +327,13 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf'])
motionPreamble(), motionPreamble(),
motionText(), motionText(),
]; ];
if (motionReason()) { var reason = motionReason();
content.push(motionReason()); if (reason) {
content.push(reason);
}
var comments = motionComments();
if (comments) {
content.push(comments);
} }
return content; return content;
}; };
@ -319,17 +352,17 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf'])
.factory('PollContentProvider', [ .factory('PollContentProvider', [
'PDFLayout', 'PDFLayout',
'gettextCatalog',
'Config', 'Config',
'User', 'User',
function(PDFLayout, Config, User) { function(PDFLayout, gettextCatalog, Config, User) {
/** /**
* Generates a content provider for polls * Generates a content provider for polls
* @constructor * @constructor
* @param {string} title - title of poll * @param {string} title - title of poll
* @param {string} id - if 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 * Returns a single section on the ballot paper
@ -440,9 +473,8 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf'])
* 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
*/ */
var createInstance = function(allMotions, $scope) { var createInstance = function(allMotions) {
var title = PDFLayout.createTitle( var title = PDFLayout.createTitle(
Config.translate(Config.get('motions_export_title').value) Config.translate(Config.get('motions_export_title').value)
@ -566,6 +598,101 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf'])
return { return {
createInstance: createInstance 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);
},
};
}
]);
}()); }());

View File

@ -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). // Cache for MotionPollDetailCtrl so that users choices are keeped during user actions (e. g. save poll form).
.value('MotionPollDetailCtrlCache', {}) .value('MotionPollDetailCtrlCache', {})
@ -672,24 +845,15 @@ angular.module('OpenSlidesApp.motions.site', [
'User', 'User',
'Agenda', 'Agenda',
'MotionBlock', 'MotionBlock',
'MotionChangeRecommendation',
'MotionCsvExport',
'MotionDocxExport',
'MotionContentProvider',
'MotionCatalogContentProvider',
'PdfMakeConverter',
'PdfMakeDocumentProvider',
'HTMLValidizer',
'Projector', 'Projector',
'ProjectionDefault', 'ProjectionDefault',
'osTableFilter', 'osTableFilter',
'osTableSort', 'osTableSort',
'PdfCreate', 'MotionExportForm',
'MotionPdfExport',
function($scope, $state, $http, gettext, gettextCatalog, ngDialog, MotionForm, Motion, function($scope, $state, $http, gettext, gettextCatalog, ngDialog, MotionForm, Motion,
Category, Config, Tag, Workflow, User, Agenda, MotionBlock, MotionChangeRecommendation, Category, Config, Tag, Workflow, User, Agenda, MotionBlock, Projector,
MotionCsvExport, MotionDocxExport, MotionContentProvider, MotionCatalogContentProvider, ProjectionDefault, osTableFilter, osTableSort, MotionExportForm, MotionPdfExport) {
PdfMakeConverter, PdfMakeDocumentProvider, HTMLValidizer, Projector, ProjectionDefault,
osTableFilter, osTableSort, PdfCreate) {
Motion.bindAll({}, $scope, 'motions'); Motion.bindAll({}, $scope, 'motions');
Category.bindAll({}, $scope, 'categories'); Category.bindAll({}, $scope, 'categories');
MotionBlock.bindAll({}, $scope, 'motionBlocks'); MotionBlock.bindAll({}, $scope, 'motionBlocks');
@ -883,52 +1047,12 @@ angular.module('OpenSlidesApp.motions.site', [
$scope.openDialog = function (motion) { $scope.openDialog = function (motion) {
ngDialog.open(MotionForm.getDialog(motion)); ngDialog.open(MotionForm.getDialog(motion));
}; };
// Export dialog
// Export as a pdf file $scope.openExportDialog = function () {
$scope.pdfExport = function() { ngDialog.open(MotionExportForm.getDialog($scope.motionsFiltered));
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);
});
}; };
$scope.pdfExport = function () {
// Export as a csv file MotionPdfExport.export($scope.motionsFiltered);
$scope.csvExport = function () {
MotionCsvExport.export($scope.motionsFiltered);
};
// Export as docx file
$scope.docxExport = function () {
MotionDocxExport.export($scope.motionsFiltered, $scope.categories);
}; };
// *** select mode functions *** // *** select mode functions ***
@ -993,11 +1117,11 @@ angular.module('OpenSlidesApp.motions.site', [
'$timeout', '$timeout',
'operator', 'operator',
'ngDialog', 'ngDialog',
'gettextCatalog',
'MotionForm', 'MotionForm',
'ChangeRecommmendationCreate', 'ChangeRecommmendationCreate',
'ChangeRecommmendationView', 'ChangeRecommmendationView',
'MotionChangeRecommendation', 'MotionChangeRecommendation',
'MotionPDFExport',
'Motion', 'Motion',
'MotionComment', 'MotionComment',
'Category', 'Category',
@ -1012,10 +1136,11 @@ angular.module('OpenSlidesApp.motions.site', [
'Projector', 'Projector',
'ProjectionDefault', 'ProjectionDefault',
'MotionBlock', 'MotionBlock',
function($scope, $http, $timeout, operator, ngDialog, MotionForm, 'MotionPdfExport',
ChangeRecommmendationCreate, ChangeRecommmendationView, MotionChangeRecommendation, MotionPDFExport, function($scope, $http, $timeout, operator, ngDialog, gettextCatalog, MotionForm,
ChangeRecommmendationCreate, ChangeRecommmendationView, MotionChangeRecommendation,
Motion, MotionComment, Category, Mediafile, Tag, User, Workflow, Config, motionId, MotionInlineEditing, Motion, MotionComment, Category, Mediafile, Tag, User, Workflow, Config, motionId, MotionInlineEditing,
MotionCommentsInlineEditing, Projector, ProjectionDefault, MotionBlock) { MotionCommentsInlineEditing, Projector, ProjectionDefault, MotionBlock, MotionPdfExport) {
var motion = Motion.get(motionId); var motion = Motion.get(motionId);
Category.bindAll({}, $scope, 'categories'); Category.bindAll({}, $scope, 'categories');
Mediafile.bindAll({}, $scope, 'mediafiles'); Mediafile.bindAll({}, $scope, 'mediafiles');
@ -1296,8 +1421,19 @@ angular.module('OpenSlidesApp.motions.site', [
$scope.viewChangeRecommendations.init($scope, Config.get('motions_recommendation_text_mode').value); $scope.viewChangeRecommendations.init($scope, Config.get('motions_recommendation_text_mode').value);
// PDF creating functions // PDF creating functions
$scope.pdfExport = MotionPDFExport; $scope.pdfExport = function () {
$scope.pdfExport.init($scope); 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);
};
} }
]) ])

View File

@ -54,7 +54,7 @@
<i class="fa fa-pencil"></i> <i class="fa fa-pencil"></i>
</a> </a>
<!-- pdf --> <!-- pdf -->
<a ng-click="pdfExport.createMotion()" class="btn btn-default btn-sm"> <a ng-click="pdfExport()" class="btn btn-default btn-sm">
<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>
@ -310,7 +310,7 @@
</button> </button>
<!-- Print poll PDF --> <!-- Print poll PDF -->
<a os-perms="motions.can_manage" ng-click="pdfExport.createPoll()" class="btn btn-default btn-xs" <a os-perms="motions.can_manage" ng-click="createPollPdf()" class="btn btn-default btn-xs"
title="{{ 'Print ballot paper' | translate }}"> title="{{ 'Print ballot paper' | translate }}">
<i class="fa fa-file-pdf-o"></i> <i class="fa fa-file-pdf-o"></i>
</a> </a>

View File

@ -0,0 +1,14 @@
<h1 translate ng-if="!singleMotion && motions.length !== 1">Export motions</h1>
<h1 translate ng-if="!singleMotion && motions.length === 1">Export motion</h1>
<h1 ng-if="singleMotion"><translate>Export motion</translate> "{{ motions.getTitle() }}"</h1>
<form name="motionPdfExportForm" ng-submit="export(params)">
<formly-form model="params" fields="formFields">
<button type="submit" ng-disabled="motionPdfExportForm.$invalid" class="btn btn-primary" translate>
Export as {{ params.format.toUpperCase() | translate }}
</button>
<button ng-click="closeThisDialog()" class="btn btn-default" translate>
Cancel
</button>
</formly-form>
</form>

View File

@ -36,43 +36,27 @@
<i class="fa fa-check-square-o"></i> <i class="fa fa-check-square-o"></i>
<translate>Select ...</translate> <translate>Select ...</translate>
</button> </button>
<!-- Export dropdown --> <!-- Export button -->
<div class="dropdown pull-right" uib-dropdown> <button type="button" class="btn btn-default btn-sm pull-right"
<button type="button" class="btn btn-default btn-sm" id="dropdownExport" uib-dropdown-toggle> os-perms="motions.can_manage" ng-click="openExportDialog()">
<i class="fa fa-upload"></i> <i class="fa fa-upload"></i>
<span ng-if="motionsFiltered.length == motions.length" translate> <span ng-if="motionsFiltered.length === motions.length" translate>
Export all Export all
</span> </span>
<span ng-if="motionsFiltered.length != motions.length" translate> <span ng-if="motionsFiltered.length !== motions.length" translate>
Export filtered Export filtered
</span> </span>
<span class="caret"></span> </button>
</button> <button type="button" class="btn btn-default btn-sm pull-right"
<ul class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownExport"> os-perms="!motions.can_manage" ng-click="pdfExport()">
<!-- PDF export --> <i class="fa fa-file-pdf-o"></i>
<li> <span ng-if="motionsFiltered.length === motions.length" translate>
<a href="" ng-click="pdfExport()"> Export all
<i class="fa fa-file-pdf-o"></i> </span>
PDF <span ng-if="motionsFiltered.length !== motions.length" translate>
</a> Export filtered
</li> </span>
<!-- CSV export --> </button>
<li os-perms="motions.can_manage">
<a href="" id="downloadLinkCSV"
ng-click="csvExport()">
<i class="fa fa-file-text-o"></i>
CSV
</a>
</li>
<!-- DOCX export -->
<li os-perms="motions.can_manage">
<a href="" ng-click="docxExport()">
<i class="fa fa-file-word-o"></i>
DOCX
</a>
</li>
</ul>
</div>
</div> </div>
</div> </div>