Export dialog for motions
This commit is contained in:
parent
4ffbe77814
commit
c68d3f0e4a
@ -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)
|
||||
==========================
|
||||
|
@ -1346,6 +1346,10 @@ img {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.btn-group > label > input[type=radio] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* user details */
|
||||
.user_details fieldset {
|
||||
margin-bottom: 10px;
|
||||
|
@ -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',
|
||||
|
17
openslides/core/static/templates/core/select-radio.html
Normal file
17
openslides/core/static/templates/core/select-radio.html
Normal 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>
|
@ -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 : '';
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
@ -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',
|
||||
|
@ -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);
|
||||
},
|
||||
};
|
||||
}
|
||||
]);
|
||||
|
||||
}());
|
||||
|
@ -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);
|
||||
};
|
||||
}
|
||||
])
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
@ -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>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user