Used worker for pdf generation.

Moved pdfmake.createPdf() into a web worker thread to prevent
blocking UI and max_script_runtime error in browser.

Used gulp to manage separate worker files (pdf-worker and pdf-worker-lib).
This commit is contained in:
FinnStutzenstein 2017-01-27 13:38:49 +01:00 committed by Emanuel Schütze
parent 872f05ec31
commit e1b4c1fc68
13 changed files with 227 additions and 44 deletions

View File

@ -18,6 +18,7 @@ Core:
- Added control for the resolution of the projectors. - Added control for the resolution of the projectors.
- Set the projector language in the settings. - Set the projector language in the settings.
- Used pdfMake for clientside generation of PDFs. - Used pdfMake for clientside generation of PDFs.
Run pdf creation in background (in a web worker thread).
- Introduced new table design for list views with serveral filters and - Introduced new table design for list views with serveral filters and
CSV export. CSV export.
- Used session cookies and store filter settings in session storage. - Used session cookies and store filter settings in session storage.

View File

@ -39,10 +39,7 @@
}, },
"overrides": { "overrides": {
"pdfmake": { "pdfmake": {
"main": [ "main": []
"build/pdfmake.js",
"build/vfs_fonts.js"
]
}, },
"pdfjs-dist": { "pdfjs-dist": {
"main": "build/pdf.combined.js" "main": "build/pdf.combined.js"

View File

@ -41,10 +41,13 @@ var output_directory = path.join('openslides', 'static');
* Default tasks to be run before start. * Default tasks to be run before start.
*/ */
// Catches all JavaScript files from all core apps and concats them to one // Catches all JavaScript files (excluded worker files) from all core apps and concats them to one
// file js/openslides.js. In production mode the file is uglified. // file js/openslides.js. In production mode the file is uglified.
gulp.task('js', function () { gulp.task('js', function () {
return gulp.src(path.join('openslides', '*', 'static', 'js', '**', '*.js')) return gulp.src([
path.join('openslides', '*', 'static', 'js', '**', '*.js'),
'!' + path.join('openslides', 'core', 'static', 'js', 'core', 'pdf-worker.js'),
])
.pipe(sourcemaps.init()) .pipe(sourcemaps.init())
.pipe(concat('openslides.js')) .pipe(concat('openslides.js'))
.pipe(sourcemaps.write()) .pipe(sourcemaps.write())
@ -68,6 +71,24 @@ gulp.task('js-libs', function () {
.pipe(gulp.dest(path.join(output_directory, 'js'))); .pipe(gulp.dest(path.join(output_directory, 'js')));
}); });
// Catches all pdfmake files for pdf worker.
gulp.task('pdf-worker', function () {
return gulp.src([
path.join('openslides', 'core', 'static', 'js', 'core', 'pdf-worker.js'),
])
.pipe(gulpif(argv.production, uglify()))
.pipe(gulp.dest(path.join(output_directory, 'js', 'workers')));
});
// pdfmake files
gulp.task('pdf-worker-libs', function () {
return gulp.src([
path.join('bower_components', 'pdfmake', 'build', 'pdfmake.min.js'),
path.join('bower_components', 'pdfmake', 'build', 'vfs_fonts.js'),
])
.pipe(concat('pdf-worker-libs.js'))
.pipe(gulp.dest(path.join(output_directory, 'js', 'workers')));
});
// Catches all template files from all core apps and concats them to one // Catches all template files from all core apps and concats them to one
// file js/openslides-templates.js. In production mode the file is uglified. // file js/openslides-templates.js. In production mode the file is uglified.
gulp.task('templates', function () { gulp.task('templates', function () {
@ -191,6 +212,8 @@ gulp.task('translations', function () {
gulp.task('default', [ gulp.task('default', [
'js', 'js',
'js-libs', 'js-libs',
'pdf-worker',
'pdf-worker-libs',
'templates', 'templates',
'css-libs', 'css-libs',
'fonts-libs', 'fonts-libs',

View File

@ -82,9 +82,10 @@ angular.module('OpenSlidesApp.agenda.site', [
'gettext', 'gettext',
'osTableFilter', 'osTableFilter',
'AgendaCsvExport', 'AgendaCsvExport',
'PdfCreate',
function($scope, $filter, $http, $state, DS, operator, ngDialog, Agenda, TopicForm, AgendaTree, Projector, function($scope, $filter, $http, $state, DS, operator, ngDialog, Agenda, TopicForm, AgendaTree, Projector,
ProjectionDefault, AgendaContentProvider, PdfMakeDocumentProvider, gettextCatalog, gettext, osTableFilter, ProjectionDefault, AgendaContentProvider, PdfMakeDocumentProvider, gettextCatalog, gettext, osTableFilter,
AgendaCsvExport) { AgendaCsvExport, PdfCreate) {
// Bind agenda tree to the scope // Bind agenda tree to the scope
$scope.$watch(function () { $scope.$watch(function () {
return Agenda.lastModified(); return Agenda.lastModified();
@ -266,7 +267,7 @@ angular.module('OpenSlidesApp.agenda.site', [
var filename = gettextCatalog.getString('Agenda') + '.pdf'; var filename = gettextCatalog.getString('Agenda') + '.pdf';
var agendaContentProvider = AgendaContentProvider.createInstance($scope.itemsFiltered); var agendaContentProvider = AgendaContentProvider.createInstance($scope.itemsFiltered);
var documentProvider = PdfMakeDocumentProvider.createInstance(agendaContentProvider); var documentProvider = PdfMakeDocumentProvider.createInstance(agendaContentProvider);
pdfMake.createPdf(documentProvider.getDocument()).download(filename); PdfCreate.download(documentProvider.getDocument(), filename);
}; };
$scope.csvExport = function () { $scope.csvExport = function () {
var element = document.getElementById('downloadLinkCSV'); var element = document.getElementById('downloadLinkCSV');

View File

@ -264,9 +264,10 @@ angular.module('OpenSlidesApp.assignments.site', [
'osTableSort', 'osTableSort',
'gettext', 'gettext',
'phases', 'phases',
'PdfCreate',
function($scope, ngDialog, AssignmentForm, Assignment, Tag, Agenda, Projector, ProjectionDefault, function($scope, ngDialog, AssignmentForm, Assignment, Tag, Agenda, Projector, ProjectionDefault,
gettextCatalog, AssignmentContentProvider, AssignmentCatalogContentProvider, PdfMakeDocumentProvider, gettextCatalog, AssignmentContentProvider, AssignmentCatalogContentProvider, PdfMakeDocumentProvider,
User, osTableFilter, osTableSort, gettext, phases) { User, osTableFilter, osTableSort, gettext, phases, PdfCreate) {
Assignment.bindAll({}, $scope, 'assignments'); Assignment.bindAll({}, $scope, 'assignments');
Tag.bindAll({}, $scope, 'tags'); Tag.bindAll({}, $scope, 'tags');
$scope.$watch(function () { $scope.$watch(function () {
@ -386,7 +387,7 @@ angular.module('OpenSlidesApp.assignments.site', [
AssignmentCatalogContentProvider.createInstance(assignmentContentProviderArray); AssignmentCatalogContentProvider.createInstance(assignmentContentProviderArray);
var documentProvider = var documentProvider =
PdfMakeDocumentProvider.createInstance(assignmentCatalogContentProvider); PdfMakeDocumentProvider.createInstance(assignmentCatalogContentProvider);
pdfMake.createPdf(documentProvider.getDocument()).download(filename); PdfCreate.download(documentProvider.getDocument(), filename);
}; };
} }
]) ])
@ -411,9 +412,10 @@ angular.module('OpenSlidesApp.assignments.site', [
'PdfMakeDocumentProvider', 'PdfMakeDocumentProvider',
'PdfMakeBallotPaperProvider', 'PdfMakeBallotPaperProvider',
'gettextCatalog', 'gettextCatalog',
'PdfCreate',
function($scope, $http, $filter, filterFilter, gettext, ngDialog, AssignmentForm, operator, Assignment, function($scope, $http, $filter, filterFilter, gettext, ngDialog, AssignmentForm, operator, Assignment,
User, assignmentId, phases, Projector, ProjectionDefault, AssignmentContentProvider, BallotContentProvider, User, assignmentId, phases, Projector, ProjectionDefault, AssignmentContentProvider, BallotContentProvider,
PdfMakeDocumentProvider, PdfMakeBallotPaperProvider, gettextCatalog) { PdfMakeDocumentProvider, PdfMakeBallotPaperProvider, gettextCatalog, PdfCreate) {
var assignment = Assignment.get(assignmentId); var assignment = Assignment.get(assignmentId);
User.bindAll({}, $scope, 'users'); User.bindAll({}, $scope, 'users');
Assignment.loadRelations(assignment, 'agenda_item'); Assignment.loadRelations(assignment, 'agenda_item');
@ -593,7 +595,7 @@ angular.module('OpenSlidesApp.assignments.site', [
var filename = gettextCatalog.getString("Election") + "_" + $scope.assignment.title + ".pdf"; var filename = gettextCatalog.getString("Election") + "_" + $scope.assignment.title + ".pdf";
var assignmentContentProvider = AssignmentContentProvider.createInstance(assignment); var assignmentContentProvider = AssignmentContentProvider.createInstance(assignment);
var documentProvider = PdfMakeDocumentProvider.createInstance(assignmentContentProvider); var documentProvider = PdfMakeDocumentProvider.createInstance(assignmentContentProvider);
pdfMake.createPdf(documentProvider.getDocument()).download(filename); PdfCreate.download(documentProvider.getDocument(), filename);
}; };
//creates the ballotpaper as pdf //creates the ballotpaper as pdf
@ -609,7 +611,7 @@ angular.module('OpenSlidesApp.assignments.site', [
var filename = gettextCatalog.getString("Ballot") + "_" + pollNumber + "_" + $scope.assignment.title + ".pdf"; var filename = gettextCatalog.getString("Ballot") + "_" + pollNumber + "_" + $scope.assignment.title + ".pdf";
var ballotContentProvider = BallotContentProvider.createInstance($scope, thePoll, pollNumber); var ballotContentProvider = BallotContentProvider.createInstance($scope, thePoll, pollNumber);
var documentProvider = PdfMakeBallotPaperProvider.createInstance(ballotContentProvider); var documentProvider = PdfMakeBallotPaperProvider.createInstance(ballotContentProvider);
pdfMake.createPdf(documentProvider.getDocument()).download(filename); PdfCreate.download(documentProvider.getDocument(), filename);
}; };
// Just mark some vote value strings for translation. // Just mark some vote value strings for translation.

View File

@ -1133,6 +1133,39 @@ img {
font-size: 90%; font-size: 90%;
} }
/** Pdf creation status bar **/
#pdf-status {
position: fixed;
bottom: 0;
width: 100%;
z-index: 100;
}
#pdf-status-container {
margin: 0 auto 0 auto;
padding: 0px 20px;
max-width: 1400px;
}
#pdf-status-container > div {
margin-bottom: 10px;
padding: 10px 20px;
border-radius: 6px;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}
#pdf-status .generating {
color: #222;
background-color: #bed4de;
border-color: #46b8da;
}
#pdf-status .error {
color: #a94442;
background-color: #f2dede;
border-color: #ebccd1;
}
#pdf-status .finished {
color: #3c763d;
background-color: #dff0d8;
border-color: #d6e9c6;
}
/** General helper classes **/ /** General helper classes **/

View File

@ -0,0 +1,37 @@
/* Worker for creating PDFs in a separate thread. The creation of larger PDFs
* needs (currently) a lot of time and we don't want to block the UI.
*/
// Setup fake environment for pdfMake
var document = {
'createElementNS': function () {
return {};
},
};
var window = this;
// PdfMake and Fonts
importScripts('/static/js/workers/pdf-worker-libs.js');
// Set default font family.
// To use custom ttf font files you have to replace the vfs_fonts.js file.
// See https://github.com/pdfmake/pdfmake/wiki/Custom-Fonts---client-side
// "PdfFont" is used as generic name in core/pdf.js. Adjust the four
// font style names only.
pdfMake.fonts = {
PdfFont: {
normal: 'Roboto-Regular.ttf',
bold: 'Roboto-Medium.ttf',
italics: 'Roboto-Italic.ttf',
bolditalics: 'Roboto-Italic.ttf'
}
};
// Create PDF on message and return the base64 decoded document
self.addEventListener('message', function(e) {
var data = JSON.parse(e.data);
var pdf = pdfMake.createPdf(data);
pdf.getBase64(function (base64) {
self.postMessage(base64);
});
}, false);

View File

@ -15,22 +15,6 @@ angular.module('OpenSlidesApp.core.pdf', [])
size: 8 size: 8
}; };
// Set and return default font family.
//
// To use custom ttf font files you have to replace the vfs_fonts.js file.
// See https://github.com/pdfmake/pdfmake/wiki/Custom-Fonts---client-side
PDFLayout.getFontName = function() {
pdfMake.fonts = {
Roboto: {
normal: 'Roboto-Regular.ttf',
bold: 'Roboto-Medium.ttf',
italics: 'Roboto-Italic.ttf',
bolditalics: 'Roboto-Italic.ttf'
}
};
return "Roboto";
};
// page title // page title
PDFLayout.createTitle = function(title) { PDFLayout.createTitle = function(title) {
return { return {
@ -229,7 +213,7 @@ angular.module('OpenSlidesApp.core.pdf', [])
pageSize: 'A4', pageSize: 'A4',
pageMargins: [80, 90, 80, 60], pageMargins: [80, 90, 80, 60],
defaultStyle: { defaultStyle: {
font: PDFLayout.getFontName(), font: 'PdfFont',
fontSize: 10 fontSize: 10
}, },
header: header, header: header,
@ -353,7 +337,7 @@ angular.module('OpenSlidesApp.core.pdf', [])
pageSize: 'A4', pageSize: 'A4',
pageMargins: [0, 0, 0, 0], pageMargins: [0, 0, 0, 0],
defaultStyle: { defaultStyle: {
font: PDFLayout.getFontName(), font: 'PdfFont',
fontSize: 10 fontSize: 10
}, },
content: content, content: content,
@ -387,9 +371,8 @@ angular.module('OpenSlidesApp.core.pdf', [])
* Converter component for HTML->JSON for pdfMake * Converter component for HTML->JSON for pdfMake
* @constructor * @constructor
* @param {object} images - Key-Value structure representing image.src/BASE64 of images * @param {object} images - Key-Value structure representing image.src/BASE64 of images
* @param {object} pdfMake - the converter component enhances pdfMake
*/ */
var createInstance = function(images, pdfMake) { var createInstance = function(images) {
var slice = Function.prototype.call.bind([].slice), var slice = Function.prototype.call.bind([].slice),
map = Function.prototype.call.bind([].map), map = Function.prototype.call.bind([].map),
@ -820,6 +803,82 @@ angular.module('OpenSlidesApp.core.pdf', [])
return { return {
createInstance: createInstance createInstance: createInstance
}; };
}]); }])
.factory('PdfCreate', [
'$timeout',
'FileSaver',
function ($timeout, FileSaver) {
var stateChangeCallbacks = [];
var b64toBlob = function(b64Data) {
var byteCharacters = atob(b64Data);
var byteNumbers = new Array(byteCharacters.length);
for (var i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
var blob = new Blob([byteArray]);
return blob;
};
var stateChange = function (state, filename, error) {
_.forEach(stateChangeCallbacks, function (cb) {
$timeout(function () {cb(state, filename, error);}, 1);
});
};
return {
download: function (pdfDocument, filename) {
stateChange('generating', filename);
var pdfWorker = new Worker('/static/js/workers/pdf-worker.js');
pdfWorker.addEventListener('message', function (event) {
var blob = b64toBlob(event.data);
stateChange('finished', filename);
FileSaver.saveAs(blob, filename);
});
pdfWorker.addEventListener('error', function (event) {
stateChange('error', filename, event.message);
});
pdfWorker.postMessage(JSON.stringify(pdfDocument));
},
registerStateChangeCallback: function (cb) {
if (cb && typeof cb === 'function') {
stateChangeCallbacks.push(cb);
}
},
};
}
])
.directive('pdfGenerationStatus', [
'$timeout',
'PdfCreate',
function($timeout, PdfCreate) {
return {
restrict: 'E',
templateUrl: 'static/templates/pdf-status.html',
scope: {},
controller: function ($scope, $element, $attrs, $location) {
$scope.pdfs = {};
var createStateChange = function (state, filename, error) {
$scope.pdfs[filename] = {
state: state,
errorMessage: error
};
if (state === 'finished') {
$timeout(function () {
$scope.close(filename);
}, 3000);
}
};
PdfCreate.registerStateChangeCallback(createStateChange);
$scope.close = function (filename) {
delete $scope.pdfs[filename];
};
},
};
}
]);
}()); }());

View File

@ -212,6 +212,8 @@
</div><!--end content-container--> </div><!--end content-container-->
</div><!--end content--> </div><!--end content-->
<pdf-generation-status></pdf-generation-status>
</div><!--end wrapper--> </div><!--end wrapper-->
<script src="/webclient/site/"></script> <script src="/webclient/site/"></script>

View File

@ -0,0 +1,24 @@
<div id="pdf-status">
<div id="pdf-status-container">
<div ng-repeat="(filename, pdf) in pdfs" ng-class="pdf.state">
<span class="close fa fa-times fa-lg" ng-click="close(filename)"></span>
<span ng-if="pdf.state === 'generating'">
<i class="fa fa-spinner fa-pulse fa-lg spacer-right"></i>
<translate>Generating PDF file {{ filename }} ...</translate>
</span>
<span ng-if="pdf.state === 'finished'">
<i class="fa fa-check fa-lg spacer-right"></i>
<translate>PDF successfully generated.</translate>
</span>
<span ng-if="pdf.state === 'error'">
<i class="fa fa-exclamation-triangle fa-lg spacer-right"></i>
<translate>Error while generating PDF file</translate> {{ filename }}:
<span ng-if="pdf.errorMessage"><code>{{ pdf.errorMessage | translate }}</code></span>
</span>
</div>
</div>
</div>

View File

@ -15,8 +15,9 @@ angular.module('OpenSlidesApp.motions.motionservices', ['OpenSlidesApp.motions',
'PollContentProvider', 'PollContentProvider',
'gettextCatalog', 'gettextCatalog',
'$http', '$http',
'PdfCreate',
function (HTMLValidizer, Motion, User, PdfMakeConverter, PdfMakeDocumentProvider, PdfMakeBallotPaperProvider, function (HTMLValidizer, Motion, User, PdfMakeConverter, PdfMakeDocumentProvider, PdfMakeBallotPaperProvider,
MotionContentProvider, PollContentProvider, gettextCatalog, $http) { MotionContentProvider, PollContentProvider, gettextCatalog, $http, PdfCreate) {
var obj = {}; var obj = {};
var $scope; var $scope;
@ -30,11 +31,11 @@ angular.module('OpenSlidesApp.motions.motionservices', ['OpenSlidesApp.motions',
}); });
$http.post('/core/encode_media/', JSON.stringify(image_sources)).success(function(data) { $http.post('/core/encode_media/', JSON.stringify(image_sources)).success(function(data) {
var converter = PdfMakeConverter.createInstance(data.images, pdfMake); var converter = PdfMakeConverter.createInstance(data.images);
var motionContentProvider = MotionContentProvider.createInstance(converter, $scope.motion, $scope, User, $http); var motionContentProvider = MotionContentProvider.createInstance(converter, $scope.motion, $scope, User, $http);
var documentProvider = PdfMakeDocumentProvider.createInstance(motionContentProvider); var documentProvider = PdfMakeDocumentProvider.createInstance(motionContentProvider);
var filename = gettextCatalog.getString("Motion") + "-" + $scope.motion.identifier + ".pdf"; var filename = gettextCatalog.getString("Motion") + "-" + $scope.motion.identifier + ".pdf";
pdfMake.createPdf(documentProvider.getDocument()).download(filename); PdfCreate.download(documentProvider.getDocument(), filename);
}); });
}; };
@ -45,7 +46,7 @@ angular.module('OpenSlidesApp.motions.motionservices', ['OpenSlidesApp.motions',
var filename = gettextCatalog.getString("Motion") + "-" + id + "-" + gettextCatalog.getString("ballot-paper") + ".pdf"; var filename = gettextCatalog.getString("Motion") + "-" + id + "-" + gettextCatalog.getString("ballot-paper") + ".pdf";
var pollContentProvider = PollContentProvider.createInstance(title, id, gettextCatalog); var pollContentProvider = PollContentProvider.createInstance(title, id, gettextCatalog);
var documentProvider = PdfMakeBallotPaperProvider.createInstance(pollContentProvider); var documentProvider = PdfMakeBallotPaperProvider.createInstance(pollContentProvider);
pdfMake.createPdf(documentProvider.getDocument()).download(filename); PdfCreate.download(documentProvider.getDocument(), filename);
}; };
obj.init = function (_scope) { obj.init = function (_scope) {

View File

@ -643,10 +643,11 @@ angular.module('OpenSlidesApp.motions.site', [
'ProjectionDefault', 'ProjectionDefault',
'osTableFilter', 'osTableFilter',
'osTableSort', 'osTableSort',
'PdfCreate',
function($scope, $state, $http, gettext, gettextCatalog, ngDialog, MotionForm, Motion, function($scope, $state, $http, gettext, gettextCatalog, ngDialog, MotionForm, Motion,
Category, Tag, Workflow, User, Agenda, MotionBlock, MotionCsvExport, MotionDocxExport, Category, Tag, Workflow, User, Agenda, MotionBlock, MotionCsvExport, MotionDocxExport,
MotionContentProvider, MotionCatalogContentProvider, PdfMakeConverter, PdfMakeDocumentProvider, MotionContentProvider, MotionCatalogContentProvider, PdfMakeConverter, PdfMakeDocumentProvider,
HTMLValidizer, Projector, ProjectionDefault, osTableFilter, osTableSort) { 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');
@ -863,7 +864,7 @@ angular.module('OpenSlidesApp.motions.site', [
//post-request to convert the images. Async. //post-request to convert the images. Async.
$http.post('/core/encode_media/', JSON.stringify(image_sources)).success(function(data) { $http.post('/core/encode_media/', JSON.stringify(image_sources)).success(function(data) {
var converter = PdfMakeConverter.createInstance(data.images, pdfMake); var converter = PdfMakeConverter.createInstance(data.images);
var motionContentProviderArray = []; var motionContentProviderArray = [];
//convert the filtered motions to motionContentProviders //convert the filtered motions to motionContentProviders
@ -872,7 +873,8 @@ angular.module('OpenSlidesApp.motions.site', [
}); });
var motionCatalogContentProvider = MotionCatalogContentProvider.createInstance(motionContentProviderArray, $scope, User, Category); var motionCatalogContentProvider = MotionCatalogContentProvider.createInstance(motionContentProviderArray, $scope, User, Category);
var documentProvider = PdfMakeDocumentProvider.createInstance(motionCatalogContentProvider); var documentProvider = PdfMakeDocumentProvider.createInstance(motionCatalogContentProvider);
pdfMake.createPdf(documentProvider.getDocument()).download(filename);
PdfCreate.download(documentProvider.getDocument(), filename);
}); });
}; };

View File

@ -425,9 +425,10 @@ angular.module('OpenSlidesApp.users.site', [
'osTableFilter', 'osTableFilter',
'osTableSort', 'osTableSort',
'gettext', 'gettext',
'PdfCreate',
function($scope, $state, $http, ngDialog, UserForm, User, Group, PasswordGenerator, Projector, ProjectionDefault, function($scope, $state, $http, ngDialog, UserForm, User, Group, PasswordGenerator, Projector, ProjectionDefault,
UserListContentProvider, Config, UserAccessDataListContentProvider, PdfMakeDocumentProvider, gettextCatalog, UserListContentProvider, Config, UserAccessDataListContentProvider, PdfMakeDocumentProvider, gettextCatalog,
UserCsvExport, osTableFilter, osTableSort, gettext) { UserCsvExport, osTableFilter, osTableSort, gettext, PdfCreate) {
User.bindAll({}, $scope, 'users'); User.bindAll({}, $scope, 'users');
Group.bindAll({where: {id: {'>': 1}}}, $scope, 'groups'); Group.bindAll({where: {id: {'>': 1}}}, $scope, 'groups');
$scope.$watch(function () { $scope.$watch(function () {
@ -623,7 +624,7 @@ angular.module('OpenSlidesApp.users.site', [
var filename = gettextCatalog.getString("List of participants")+".pdf"; var filename = gettextCatalog.getString("List of participants")+".pdf";
var userListContentProvider = UserListContentProvider.createInstance($scope.usersFiltered, $scope.groups); var userListContentProvider = UserListContentProvider.createInstance($scope.usersFiltered, $scope.groups);
var documentProvider = PdfMakeDocumentProvider.createInstance(userListContentProvider); var documentProvider = PdfMakeDocumentProvider.createInstance(userListContentProvider);
pdfMake.createPdf(documentProvider.getDocument()).download(filename); PdfCreate.download(documentProvider.getDocument(), filename);
}; };
$scope.pdfExportUserAccessDataList = function () { $scope.pdfExportUserAccessDataList = function () {
var filename = gettextCatalog.getString("List of access data")+".pdf"; var filename = gettextCatalog.getString("List of access data")+".pdf";
@ -631,7 +632,7 @@ angular.module('OpenSlidesApp.users.site', [
$scope.usersFiltered, $scope.groups, Config); $scope.usersFiltered, $scope.groups, Config);
var documentProvider = PdfMakeDocumentProvider.createInstance(userAccessDataListContentProvider); var documentProvider = PdfMakeDocumentProvider.createInstance(userAccessDataListContentProvider);
var noFooter = true; var noFooter = true;
pdfMake.createPdf(documentProvider.getDocument(noFooter)).download(filename); PdfCreate.download(documentProvider.getDocument(noFooter), filename);
}; };
// Export as a csv file // Export as a csv file
$scope.csvExport = function () { $scope.csvExport = function () {