Export dialog for motions

This commit is contained in:
FinnStutzenstein 2017-03-31 14:59:31 +02:00
parent 4ffbe77814
commit c68d3f0e4a
12 changed files with 517 additions and 243 deletions

View File

@ -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)
==========================

View File

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

View File

@ -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',

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', [
'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 : '';

View File

@ -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 = '<w:p><w:r><w:br w:type="page" /></w:r></w:p>';
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);
});
});
},

View File

@ -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',

View File

@ -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);
},
};
}
]);
}());

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).
.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);
};
}
])

View File

@ -54,7 +54,7 @@
<i class="fa fa-pencil"></i>
</a>
<!-- 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>
<translate>PDF</translate>
</a>
@ -310,7 +310,7 @@
</button>
<!-- 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 }}">
<i class="fa fa-file-pdf-o"></i>
</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>
<translate>Select ...</translate>
</button>
<!-- Export dropdown -->
<div class="dropdown pull-right" uib-dropdown>
<button type="button" class="btn btn-default btn-sm" id="dropdownExport" uib-dropdown-toggle>
<i class="fa fa-upload"></i>
<span ng-if="motionsFiltered.length == motions.length" translate>
Export all
</span>
<span ng-if="motionsFiltered.length != motions.length" translate>
Export filtered
</span>
<span class="caret"></span>
</button>
<ul class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownExport">
<!-- PDF export -->
<li>
<a href="" ng-click="pdfExport()">
<i class="fa fa-file-pdf-o"></i>
PDF
</a>
</li>
<!-- CSV export -->
<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>
<!-- Export button -->
<button type="button" class="btn btn-default btn-sm pull-right"
os-perms="motions.can_manage" ng-click="openExportDialog()">
<i class="fa fa-upload"></i>
<span ng-if="motionsFiltered.length === motions.length" translate>
Export all
</span>
<span ng-if="motionsFiltered.length !== motions.length" translate>
Export filtered
</span>
</button>
<button type="button" class="btn btn-default btn-sm pull-right"
os-perms="!motions.can_manage" ng-click="pdfExport()">
<i class="fa fa-file-pdf-o"></i>
<span ng-if="motionsFiltered.length === motions.length" translate>
Export all
</span>
<span ng-if="motionsFiltered.length !== motions.length" translate>
Export filtered
</span>
</button>
</div>
</div>