2015-11-23 21:31:15 +01:00
|
|
|
(function () {
|
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
2016-10-05 11:27:22 +02:00
|
|
|
angular.module('OpenSlidesApp.motions.site', [
|
|
|
|
'OpenSlidesApp.motions',
|
|
|
|
'OpenSlidesApp.motions.diff',
|
|
|
|
'OpenSlidesApp.motions.motionservices',
|
|
|
|
'OpenSlidesApp.core.pdf',
|
|
|
|
'OpenSlidesApp.motions.pdf'
|
|
|
|
])
|
2016-09-20 16:29:42 +02:00
|
|
|
|
2015-11-23 21:31:15 +01:00
|
|
|
.config([
|
|
|
|
'mainMenuProvider',
|
|
|
|
'gettext',
|
|
|
|
function (mainMenuProvider, gettext) {
|
|
|
|
mainMenuProvider.register({
|
|
|
|
'ui_sref': 'motions.motion.list',
|
|
|
|
'img_class': 'file-text',
|
|
|
|
'title': gettext('Motions'),
|
|
|
|
'weight': 300,
|
|
|
|
'perm': 'motions.can_see',
|
|
|
|
});
|
|
|
|
}
|
|
|
|
])
|
|
|
|
|
2016-01-17 21:16:04 +01:00
|
|
|
.config([
|
|
|
|
'$stateProvider',
|
|
|
|
function($stateProvider) {
|
|
|
|
$stateProvider
|
|
|
|
.state('motions', {
|
|
|
|
url: '/motions',
|
|
|
|
abstract: true,
|
|
|
|
template: "<ui-view/>",
|
|
|
|
})
|
|
|
|
.state('motions.motion', {
|
|
|
|
abstract: true,
|
|
|
|
template: "<ui-view/>",
|
|
|
|
})
|
|
|
|
.state('motions.motion.list', {
|
|
|
|
resolve: {
|
|
|
|
motions: function(Motion) {
|
2016-01-25 21:22:22 +01:00
|
|
|
return Motion.findAll().then(function(motions) {
|
|
|
|
angular.forEach(motions, function(motion) {
|
|
|
|
Motion.loadRelations(motion, 'agenda_item');
|
|
|
|
});
|
|
|
|
});
|
2016-01-17 21:16:04 +01:00
|
|
|
},
|
|
|
|
categories: function(Category) {
|
|
|
|
return Category.findAll();
|
|
|
|
},
|
|
|
|
tags: function(Tag) {
|
|
|
|
return Tag.findAll();
|
|
|
|
},
|
|
|
|
users: function(User) {
|
2016-03-18 11:00:31 +01:00
|
|
|
return User.findAll().catch(
|
|
|
|
function () {
|
|
|
|
return null;
|
2016-07-26 16:44:22 +02:00
|
|
|
});
|
2016-01-17 21:16:04 +01:00
|
|
|
},
|
|
|
|
workflows: function(Workflow) {
|
|
|
|
return Workflow.findAll();
|
2016-07-26 16:44:22 +02:00
|
|
|
},
|
|
|
|
items: function(Agenda) {
|
|
|
|
return Agenda.findAll().catch(
|
|
|
|
function () {
|
|
|
|
return null;
|
|
|
|
});
|
2016-01-17 21:16:04 +01:00
|
|
|
}
|
2015-11-23 21:31:15 +01:00
|
|
|
}
|
2016-01-17 21:16:04 +01:00
|
|
|
})
|
|
|
|
.state('motions.motion.detail', {
|
|
|
|
resolve: {
|
|
|
|
motion: function(Motion, $stateParams) {
|
2016-03-18 11:00:31 +01:00
|
|
|
return Motion.find($stateParams.id);
|
2016-01-17 21:16:04 +01:00
|
|
|
},
|
2016-08-19 21:03:14 +02:00
|
|
|
motions: function(Motion) {
|
|
|
|
return Motion.findAll();
|
|
|
|
},
|
2016-01-17 21:16:04 +01:00
|
|
|
categories: function(Category) {
|
|
|
|
return Category.findAll();
|
|
|
|
},
|
|
|
|
users: function(User) {
|
2016-03-18 11:00:31 +01:00
|
|
|
return User.findAll().catch(
|
|
|
|
function () {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
);
|
2016-01-17 21:16:04 +01:00
|
|
|
},
|
2016-07-26 16:44:22 +02:00
|
|
|
items: function(Agenda) {
|
|
|
|
return Agenda.findAll().catch(
|
|
|
|
function () {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
},
|
2016-01-17 21:16:04 +01:00
|
|
|
mediafiles: function(Mediafile) {
|
2016-03-18 11:00:31 +01:00
|
|
|
return Mediafile.findAll().catch(
|
|
|
|
function () {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
);
|
2016-01-17 21:16:04 +01:00
|
|
|
},
|
|
|
|
tags: function(Tag) {
|
|
|
|
return Tag.findAll();
|
|
|
|
}
|
2015-11-23 21:31:15 +01:00
|
|
|
}
|
2016-01-17 21:16:04 +01:00
|
|
|
})
|
|
|
|
// redirects to motion detail and opens motion edit form dialog, uses edit url,
|
|
|
|
// used by ui-sref links from agenda only
|
|
|
|
// (from motion controller use MotionForm factory instead to open dialog in front of
|
|
|
|
// current view without redirect)
|
|
|
|
.state('motions.motion.detail.update', {
|
|
|
|
onEnter: ['$stateParams', '$state', 'ngDialog', 'Motion',
|
|
|
|
function($stateParams, $state, ngDialog, Motion) {
|
|
|
|
ngDialog.open({
|
|
|
|
template: 'static/templates/motions/motion-form.html',
|
|
|
|
controller: 'MotionUpdateCtrl',
|
|
|
|
className: 'ngdialog-theme-default wide-form',
|
|
|
|
closeByEscape: false,
|
|
|
|
closeByDocument: false,
|
|
|
|
resolve: {
|
2016-01-22 23:19:41 +01:00
|
|
|
motion: function() {
|
|
|
|
return Motion.find($stateParams.id).then(function(motion) {
|
2016-02-09 23:05:36 +01:00
|
|
|
return Motion.loadRelations(motion, 'agenda_item');
|
2016-01-22 23:19:41 +01:00
|
|
|
});
|
|
|
|
},
|
2016-07-26 16:44:22 +02:00
|
|
|
items: function(Agenda) {
|
|
|
|
return Agenda.findAll().catch(
|
|
|
|
function() {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
2016-01-17 21:16:04 +01:00
|
|
|
},
|
|
|
|
preCloseCallback: function() {
|
|
|
|
$state.go('motions.motion.detail', {motion: $stateParams.id});
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
]
|
|
|
|
})
|
|
|
|
.state('motions.motion.import', {
|
|
|
|
url: '/import',
|
|
|
|
controller: 'MotionImportCtrl',
|
|
|
|
resolve: {
|
|
|
|
motions: function(Motion) {
|
|
|
|
return Motion.findAll();
|
|
|
|
},
|
|
|
|
categories: function(Category) {
|
|
|
|
return Category.findAll();
|
|
|
|
},
|
|
|
|
users: function(User) {
|
|
|
|
return User.findAll();
|
|
|
|
}
|
2016-01-10 13:47:59 +01:00
|
|
|
}
|
2016-01-17 21:16:04 +01:00
|
|
|
})
|
|
|
|
// categories
|
|
|
|
.state('motions.category', {
|
|
|
|
url: '/category',
|
|
|
|
abstract: true,
|
|
|
|
template: "<ui-view/>",
|
|
|
|
})
|
|
|
|
.state('motions.category.list', {
|
|
|
|
resolve: {
|
|
|
|
categories: function(Category) {
|
|
|
|
return Category.findAll();
|
|
|
|
}
|
2015-11-28 19:59:58 +01:00
|
|
|
}
|
2016-01-17 21:16:04 +01:00
|
|
|
})
|
|
|
|
.state('motions.category.create', {})
|
|
|
|
.state('motions.category.detail', {
|
|
|
|
resolve: {
|
|
|
|
category: function(Category, $stateParams) {
|
|
|
|
return Category.find($stateParams.id);
|
|
|
|
}
|
2015-11-23 21:31:15 +01:00
|
|
|
}
|
2016-01-17 21:16:04 +01:00
|
|
|
})
|
|
|
|
.state('motions.category.detail.update', {
|
|
|
|
views: {
|
|
|
|
'@motions.category': {}
|
2015-11-23 21:31:15 +01:00
|
|
|
}
|
2016-08-17 12:26:27 +02:00
|
|
|
})
|
|
|
|
.state('motions.category.sort', {
|
|
|
|
url: '/sort/{id}',
|
|
|
|
resolve: {
|
|
|
|
category: function(Category, $stateParams) {
|
|
|
|
return Category.find($stateParams.id);
|
|
|
|
},
|
|
|
|
motions: function(Motion) {
|
|
|
|
return Motion.findAll();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
controller: 'CategorySortCtrl',
|
|
|
|
templateUrl: 'static/templates/motions/category-sort.html'
|
2016-01-17 21:16:04 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
])
|
2015-11-23 21:31:15 +01:00
|
|
|
|
2016-09-17 22:26:23 +02:00
|
|
|
// Load all MotionWorkflows at startup
|
|
|
|
.run([
|
|
|
|
'Workflow',
|
|
|
|
function (Workflow) {
|
|
|
|
Workflow.findAll();
|
|
|
|
}
|
|
|
|
])
|
|
|
|
|
2016-01-10 13:47:59 +01:00
|
|
|
// Service for generic motion form (create and update)
|
|
|
|
.factory('MotionForm', [
|
2015-11-30 23:52:42 +01:00
|
|
|
'gettextCatalog',
|
2015-11-27 23:09:38 +01:00
|
|
|
'operator',
|
2016-02-12 00:15:35 +01:00
|
|
|
'Editor',
|
2016-07-29 23:33:47 +02:00
|
|
|
'MotionComment',
|
2015-11-27 23:09:38 +01:00
|
|
|
'Category',
|
|
|
|
'Config',
|
|
|
|
'Mediafile',
|
|
|
|
'Tag',
|
|
|
|
'User',
|
|
|
|
'Workflow',
|
2016-07-26 16:44:22 +02:00
|
|
|
'Agenda',
|
|
|
|
'AgendaTree',
|
2016-07-29 23:33:47 +02:00
|
|
|
function (gettextCatalog, operator, Editor, MotionComment, Category, Config, Mediafile, Tag, User, Workflow, Agenda, AgendaTree) {
|
2015-11-27 23:09:38 +01:00
|
|
|
return {
|
2016-01-10 13:47:59 +01:00
|
|
|
// ngDialog for motion form
|
|
|
|
getDialog: function (motion) {
|
2016-02-24 09:42:28 +01:00
|
|
|
var resolve = {};
|
2016-01-10 13:47:59 +01:00
|
|
|
if (motion) {
|
2016-02-12 00:15:35 +01:00
|
|
|
resolve = {
|
2016-01-22 23:19:41 +01:00
|
|
|
motion: function() {
|
2016-07-29 23:33:47 +02:00
|
|
|
MotionComment.populateFields(motion);
|
2016-01-22 23:19:41 +01:00
|
|
|
return motion;
|
|
|
|
},
|
|
|
|
agenda_item: function(Motion) {
|
|
|
|
return Motion.loadRelations(motion, 'agenda_item');
|
|
|
|
}
|
2016-01-10 13:47:59 +01:00
|
|
|
};
|
|
|
|
}
|
2016-02-24 09:42:28 +01:00
|
|
|
resolve.mediafiles = function (Mediafile) {
|
|
|
|
return Mediafile.findAll();
|
|
|
|
};
|
2016-01-10 13:47:59 +01:00
|
|
|
return {
|
|
|
|
template: 'static/templates/motions/motion-form.html',
|
|
|
|
controller: (motion) ? 'MotionUpdateCtrl' : 'MotionCreateCtrl',
|
|
|
|
className: 'ngdialog-theme-default wide-form',
|
|
|
|
closeByEscape: false,
|
|
|
|
closeByDocument: false,
|
|
|
|
resolve: (resolve) ? resolve : null
|
2016-02-24 09:42:28 +01:00
|
|
|
};
|
2016-01-10 13:47:59 +01:00
|
|
|
},
|
|
|
|
// angular-formly fields for motion form
|
2015-11-27 23:09:38 +01:00
|
|
|
getFormFields: function () {
|
2016-01-29 14:52:25 +01:00
|
|
|
var workflows = Workflow.getAll();
|
2016-02-12 00:15:35 +01:00
|
|
|
var images = Mediafile.getAllImages();
|
2016-10-04 15:06:50 +02:00
|
|
|
var formFields = [
|
2015-11-27 23:09:38 +01:00
|
|
|
{
|
|
|
|
key: 'identifier',
|
|
|
|
type: 'input',
|
|
|
|
templateOptions: {
|
2015-11-30 23:52:42 +01:00
|
|
|
label: gettextCatalog.getString('Identifier')
|
2015-11-27 23:09:38 +01:00
|
|
|
},
|
|
|
|
hide: true
|
|
|
|
},
|
|
|
|
{
|
|
|
|
key: 'submitters_id',
|
2016-03-08 22:55:43 +01:00
|
|
|
type: 'select-multiple',
|
2015-11-27 23:09:38 +01:00
|
|
|
templateOptions: {
|
2015-11-30 23:52:42 +01:00
|
|
|
label: gettextCatalog.getString('Submitters'),
|
2015-11-27 23:09:38 +01:00
|
|
|
options: User.getAll(),
|
2016-03-08 22:55:43 +01:00
|
|
|
ngOptions: 'option.id as option.full_name for option in to.options',
|
2015-12-11 20:04:05 +01:00
|
|
|
placeholder: gettextCatalog.getString('Select or search a submitter ...')
|
2015-11-27 23:09:38 +01:00
|
|
|
},
|
|
|
|
hide: !operator.hasPerms('motions.can_manage')
|
|
|
|
},
|
|
|
|
{
|
|
|
|
key: 'title',
|
|
|
|
type: 'input',
|
|
|
|
templateOptions: {
|
2015-11-30 23:52:42 +01:00
|
|
|
label: gettextCatalog.getString('Title'),
|
2015-11-27 23:09:38 +01:00
|
|
|
required: true
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
key: 'text',
|
2016-02-12 00:15:35 +01:00
|
|
|
type: 'editor',
|
2015-11-27 23:09:38 +01:00
|
|
|
templateOptions: {
|
2015-11-30 23:52:42 +01:00
|
|
|
label: gettextCatalog.getString('Text'),
|
2015-11-27 23:09:38 +01:00
|
|
|
required: true
|
|
|
|
},
|
2016-02-12 00:15:35 +01:00
|
|
|
data: {
|
|
|
|
tinymceOption: Editor.getOptions(images)
|
|
|
|
}
|
2015-11-27 23:09:38 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
key: 'reason',
|
2016-02-12 00:15:35 +01:00
|
|
|
type: 'editor',
|
2015-11-27 23:09:38 +01:00
|
|
|
templateOptions: {
|
2016-02-12 00:15:35 +01:00
|
|
|
label: gettextCatalog.getString('Reason'),
|
2015-11-27 23:09:38 +01:00
|
|
|
},
|
2016-02-12 00:15:35 +01:00
|
|
|
data: {
|
|
|
|
tinymceOption: Editor.getOptions(images)
|
|
|
|
}
|
2015-11-27 23:09:38 +01:00
|
|
|
},
|
2016-01-17 22:32:12 +01:00
|
|
|
{
|
|
|
|
key: 'disable_versioning',
|
|
|
|
type: 'checkbox',
|
|
|
|
templateOptions: {
|
|
|
|
label: gettextCatalog.getString('Trivial change'),
|
|
|
|
description: gettextCatalog.getString("Don't create a new version.")
|
|
|
|
},
|
|
|
|
hide: true
|
|
|
|
},
|
2016-01-22 23:19:41 +01:00
|
|
|
{
|
|
|
|
key: 'showAsAgendaItem',
|
|
|
|
type: 'checkbox',
|
|
|
|
templateOptions: {
|
|
|
|
label: gettextCatalog.getString('Show as agenda item'),
|
|
|
|
description: gettextCatalog.getString('If deactivated the motion appears as internal item on agenda.')
|
|
|
|
},
|
|
|
|
hide: !operator.hasPerms('motions.can_manage')
|
|
|
|
},
|
2016-07-26 16:44:22 +02:00
|
|
|
{
|
|
|
|
key: 'agenda_parent_item_id',
|
|
|
|
type: 'select-single',
|
|
|
|
templateOptions: {
|
|
|
|
label: gettextCatalog.getString('Parent item'),
|
|
|
|
options: AgendaTree.getFlatTree(Agenda.getAll()),
|
|
|
|
ngOptions: 'item.id as item.getListViewTitle() for item in to.options | notself : model.agenda_item_id',
|
|
|
|
placeholder: gettextCatalog.getString('Select a parent item ...')
|
|
|
|
},
|
|
|
|
hide: !operator.hasPerms('agenda.can_manage')
|
2016-09-16 23:35:37 +02:00
|
|
|
}]
|
|
|
|
.concat(MotionComment.getFormFields())
|
|
|
|
.concat([
|
2015-11-27 23:09:38 +01:00
|
|
|
{
|
|
|
|
key: 'more',
|
|
|
|
type: 'checkbox',
|
|
|
|
templateOptions: {
|
2015-11-30 23:52:42 +01:00
|
|
|
label: gettextCatalog.getString('Show extended fields')
|
2015-11-27 23:09:38 +01:00
|
|
|
},
|
|
|
|
hide: !operator.hasPerms('motions.can_manage')
|
|
|
|
},
|
|
|
|
{
|
|
|
|
key: 'attachments_id',
|
2016-03-08 22:55:43 +01:00
|
|
|
type: 'select-multiple',
|
2015-11-27 23:09:38 +01:00
|
|
|
templateOptions: {
|
2015-11-30 23:52:42 +01:00
|
|
|
label: gettextCatalog.getString('Attachment'),
|
2015-11-27 23:09:38 +01:00
|
|
|
options: Mediafile.getAll(),
|
2016-03-08 22:55:43 +01:00
|
|
|
ngOptions: 'option.id as option.title_or_filename for option in to.options',
|
2015-12-11 20:04:05 +01:00
|
|
|
placeholder: gettextCatalog.getString('Select or search an attachment ...')
|
2015-11-27 23:09:38 +01:00
|
|
|
},
|
|
|
|
hideExpression: '!model.more'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
key: 'category_id',
|
2016-03-08 22:55:43 +01:00
|
|
|
type: 'select-single',
|
2015-11-27 23:09:38 +01:00
|
|
|
templateOptions: {
|
2015-11-30 23:52:42 +01:00
|
|
|
label: gettextCatalog.getString('Category'),
|
2015-11-27 23:09:38 +01:00
|
|
|
options: Category.getAll(),
|
2016-03-08 22:55:43 +01:00
|
|
|
ngOptions: 'option.id as option.name for option in to.options',
|
2015-12-11 20:04:05 +01:00
|
|
|
placeholder: gettextCatalog.getString('Select or search a category ...')
|
2015-11-27 23:09:38 +01:00
|
|
|
},
|
|
|
|
hideExpression: '!model.more'
|
|
|
|
},
|
2016-07-13 14:45:40 +02:00
|
|
|
{
|
|
|
|
key: 'origin',
|
|
|
|
type: 'input',
|
|
|
|
templateOptions: {
|
|
|
|
label: gettextCatalog.getString('Origin'),
|
|
|
|
},
|
|
|
|
hideExpression: '!model.more'
|
|
|
|
},
|
2015-11-27 23:09:38 +01:00
|
|
|
{
|
|
|
|
key: 'tags_id',
|
2016-03-08 22:55:43 +01:00
|
|
|
type: 'select-multiple',
|
2015-11-27 23:09:38 +01:00
|
|
|
templateOptions: {
|
2015-11-30 23:52:42 +01:00
|
|
|
label: gettextCatalog.getString('Tags'),
|
2015-11-27 23:09:38 +01:00
|
|
|
options: Tag.getAll(),
|
2016-03-08 22:55:43 +01:00
|
|
|
ngOptions: 'option.id as option.name for option in to.options',
|
2015-12-11 20:04:05 +01:00
|
|
|
placeholder: gettextCatalog.getString('Select or search a tag ...')
|
2015-11-27 23:09:38 +01:00
|
|
|
},
|
|
|
|
hideExpression: '!model.more'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
key: 'supporters_id',
|
2016-03-08 22:55:43 +01:00
|
|
|
type: 'select-multiple',
|
2015-11-27 23:09:38 +01:00
|
|
|
templateOptions: {
|
2015-11-30 23:52:42 +01:00
|
|
|
label: gettextCatalog.getString('Supporters'),
|
2015-11-27 23:09:38 +01:00
|
|
|
options: User.getAll(),
|
2016-03-08 22:55:43 +01:00
|
|
|
ngOptions: 'option.id as option.full_name for option in to.options',
|
2015-12-11 20:04:05 +01:00
|
|
|
placeholder: gettextCatalog.getString('Select or search a supporter ...')
|
2015-11-27 23:09:38 +01:00
|
|
|
},
|
|
|
|
hideExpression: '!model.more'
|
2016-07-29 23:33:47 +02:00
|
|
|
}]);
|
2016-10-04 15:06:50 +02:00
|
|
|
|
|
|
|
if (workflows.length > 1) {
|
|
|
|
formFields = formFields.concat([
|
|
|
|
{
|
|
|
|
key: 'workflow_id',
|
|
|
|
type: 'select-single',
|
|
|
|
templateOptions: {
|
|
|
|
label: gettextCatalog.getString('Workflow'),
|
|
|
|
optionsAttr: 'bs-options',
|
|
|
|
options: workflows,
|
|
|
|
ngOptions: 'option.id as option.name | translate for option in to.options',
|
|
|
|
placeholder: gettextCatalog.getString('Select or search a workflow ...')
|
|
|
|
},
|
|
|
|
hideExpression: '!model.more',
|
|
|
|
}
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return formFields;
|
2015-11-27 23:09:38 +01:00
|
|
|
}
|
2016-02-24 09:42:28 +01:00
|
|
|
};
|
2015-11-27 23:09:38 +01:00
|
|
|
}
|
|
|
|
])
|
|
|
|
|
2016-01-14 22:43:49 +01:00
|
|
|
// Provide generic motionpoll form fields for poll update view
|
|
|
|
.factory('MotionPollForm', [
|
|
|
|
'gettextCatalog',
|
|
|
|
function (gettextCatalog) {
|
|
|
|
return {
|
|
|
|
getFormFields: function () {
|
|
|
|
return [
|
|
|
|
{
|
|
|
|
key: 'yes',
|
|
|
|
type: 'input',
|
|
|
|
templateOptions: {
|
|
|
|
label: gettextCatalog.getString('Yes'),
|
|
|
|
type: 'number',
|
|
|
|
required: true
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
key: 'no',
|
|
|
|
type: 'input',
|
|
|
|
templateOptions: {
|
|
|
|
label: gettextCatalog.getString('No'),
|
|
|
|
type: 'number',
|
|
|
|
required: true
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
key: 'abstain',
|
|
|
|
type: 'input',
|
|
|
|
templateOptions: {
|
|
|
|
label: gettextCatalog.getString('Abstain'),
|
|
|
|
type: 'number',
|
|
|
|
required: true
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
key: 'votesvalid',
|
|
|
|
type: 'input',
|
|
|
|
templateOptions: {
|
2016-08-26 13:46:57 +02:00
|
|
|
label: gettextCatalog.getString('Valid ballots'),
|
2016-01-14 22:43:49 +01:00
|
|
|
type: 'number'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
key: 'votesinvalid',
|
|
|
|
type: 'input',
|
|
|
|
templateOptions: {
|
2016-08-26 13:46:57 +02:00
|
|
|
label: gettextCatalog.getString('Invalid ballots'),
|
2016-01-14 22:43:49 +01:00
|
|
|
type: 'number'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
key: 'votescast',
|
|
|
|
type: 'input',
|
|
|
|
templateOptions: {
|
2016-08-26 13:46:57 +02:00
|
|
|
label: gettextCatalog.getString('Casted ballots'),
|
2016-01-14 22:43:49 +01:00
|
|
|
type: 'number'
|
|
|
|
}
|
|
|
|
}];
|
|
|
|
}
|
2016-02-24 09:42:28 +01:00
|
|
|
};
|
2016-01-14 22:43:49 +01:00
|
|
|
}
|
|
|
|
])
|
|
|
|
|
2015-11-23 21:31:15 +01:00
|
|
|
.controller('MotionListCtrl', [
|
|
|
|
'$scope',
|
|
|
|
'$state',
|
2016-08-12 15:01:41 +02:00
|
|
|
'$http',
|
2015-11-27 23:09:38 +01:00
|
|
|
'ngDialog',
|
2016-01-10 13:47:59 +01:00
|
|
|
'MotionForm',
|
2015-11-23 21:31:15 +01:00
|
|
|
'Motion',
|
|
|
|
'Category',
|
|
|
|
'Tag',
|
|
|
|
'Workflow',
|
|
|
|
'User',
|
2016-08-12 15:01:41 +02:00
|
|
|
'Agenda',
|
2016-09-13 11:54:30 +02:00
|
|
|
'MotionDocxExport',
|
2016-09-20 16:29:42 +02:00
|
|
|
'MotionContentProvider',
|
|
|
|
'MotionCatalogContentProvider',
|
|
|
|
'PdfMakeConverter',
|
|
|
|
'PdfMakeDocumentProvider',
|
|
|
|
'gettextCatalog',
|
2016-09-26 18:53:59 +02:00
|
|
|
'HTMLValidizer',
|
2016-09-12 11:05:34 +02:00
|
|
|
'Projector',
|
|
|
|
'ProjectionDefault',
|
2016-09-20 16:29:42 +02:00
|
|
|
function($scope, $state, $http, ngDialog, MotionForm, Motion, Category, Tag, Workflow, User, Agenda, MotionDocxExport,
|
2016-09-12 11:05:34 +02:00
|
|
|
MotionContentProvider, MotionCatalogContentProvider, PdfMakeConverter, PdfMakeDocumentProvider,
|
|
|
|
gettextCatalog, HTMLValidizer, Projector, ProjectionDefault) {
|
2015-11-23 21:31:15 +01:00
|
|
|
Motion.bindAll({}, $scope, 'motions');
|
|
|
|
Category.bindAll({}, $scope, 'categories');
|
|
|
|
Tag.bindAll({}, $scope, 'tags');
|
|
|
|
Workflow.bindAll({}, $scope, 'workflows');
|
|
|
|
User.bindAll({}, $scope, 'users');
|
2016-09-12 11:05:34 +02:00
|
|
|
Projector.bindAll({}, $scope, 'projectors');
|
|
|
|
$scope.$watch(function () {
|
|
|
|
return Projector.lastModified();
|
|
|
|
}, function () {
|
|
|
|
var projectiondefault = ProjectionDefault.filter({name: 'motions'})[0];
|
|
|
|
if (projectiondefault) {
|
|
|
|
$scope.defaultProjectorId = projectiondefault.projector_id;
|
|
|
|
}
|
|
|
|
});
|
2015-11-23 21:31:15 +01:00
|
|
|
$scope.alert = {};
|
|
|
|
|
|
|
|
// setup table sorting
|
|
|
|
$scope.sortColumn = 'identifier';
|
|
|
|
$scope.filterPresent = '';
|
|
|
|
$scope.reverse = false;
|
2016-08-12 15:01:41 +02:00
|
|
|
|
|
|
|
$scope.multiselectFilter = {
|
|
|
|
state: [],
|
|
|
|
category: [],
|
|
|
|
tag: []
|
|
|
|
};
|
|
|
|
$scope.getItemId = {
|
|
|
|
state: function (motion) {return motion.state_id;},
|
|
|
|
category: function (motion) {return motion.category_id;},
|
|
|
|
tag: function (motion) {return motion.tags_id;}
|
|
|
|
};
|
|
|
|
// function to operate the multiselectFilter
|
|
|
|
$scope.operateMultiselectFilter = function (filter, id) {
|
|
|
|
if (!$scope.isDeleteMode) {
|
|
|
|
if (_.indexOf($scope.multiselectFilter[filter], id) > -1) {
|
|
|
|
// remove id
|
|
|
|
$scope.multiselectFilter[filter] = _.filter($scope.multiselectFilter[filter], function (_id) {
|
|
|
|
return _id != id;
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
// add id
|
|
|
|
$scope.multiselectFilter[filter].push(id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2015-11-23 21:31:15 +01:00
|
|
|
// function to sort by clicked column
|
2016-01-09 16:45:47 +01:00
|
|
|
$scope.toggleSort = function (column) {
|
2015-11-23 21:31:15 +01:00
|
|
|
if ( $scope.sortColumn === column ) {
|
|
|
|
$scope.reverse = !$scope.reverse;
|
|
|
|
}
|
|
|
|
$scope.sortColumn = column;
|
|
|
|
};
|
2016-01-21 23:20:14 +01:00
|
|
|
// define custom search filter string
|
2016-01-09 16:45:47 +01:00
|
|
|
$scope.getFilterString = function (motion) {
|
2016-02-24 09:42:28 +01:00
|
|
|
var category = '';
|
2016-01-09 16:45:47 +01:00
|
|
|
if (motion.category) {
|
2016-02-24 09:42:28 +01:00
|
|
|
category = motion.category.name;
|
2016-01-09 16:45:47 +01:00
|
|
|
}
|
|
|
|
return [
|
2016-01-21 23:20:14 +01:00
|
|
|
motion.identifier,
|
2016-01-09 16:45:47 +01:00
|
|
|
motion.getTitle(),
|
|
|
|
motion.getText(),
|
2016-01-21 23:20:14 +01:00
|
|
|
motion.getReason(),
|
2016-07-27 16:11:49 +02:00
|
|
|
motion.origin,
|
2016-02-24 09:42:28 +01:00
|
|
|
_.map(
|
|
|
|
motion.submitters,
|
|
|
|
function (submitter) {
|
|
|
|
return submitter.get_short_name();
|
|
|
|
}
|
|
|
|
).join(" "),
|
|
|
|
_.map(
|
|
|
|
motion.supporters,
|
|
|
|
function (supporter) {
|
|
|
|
return supporter.get_short_name();
|
|
|
|
}
|
|
|
|
).join(" "),
|
|
|
|
_.map(
|
|
|
|
motion.tags,
|
|
|
|
function (tag) {
|
|
|
|
return tag.name;
|
|
|
|
}
|
|
|
|
).join(" "),
|
|
|
|
category,
|
2016-01-21 23:20:14 +01:00
|
|
|
].join(" ");
|
2016-02-24 09:42:28 +01:00
|
|
|
};
|
2016-08-12 15:01:41 +02:00
|
|
|
// for reset-button
|
|
|
|
$scope.reset_filters = function () {
|
|
|
|
$scope.multiselectFilter = {
|
|
|
|
state: [],
|
|
|
|
category: [],
|
|
|
|
tag: []
|
|
|
|
};
|
|
|
|
if ($scope.filter) {
|
|
|
|
$scope.filter.search = '';
|
|
|
|
}
|
|
|
|
};
|
|
|
|
$scope.are_filters_set = function () {
|
|
|
|
return $scope.multiselectFilter.state.length > 0 ||
|
|
|
|
$scope.multiselectFilter.category.length > 0 ||
|
|
|
|
$scope.multiselectFilter.tag.length > 0 ||
|
|
|
|
($scope.filter ? $scope.filter.search : false);
|
|
|
|
};
|
2015-11-23 21:31:15 +01:00
|
|
|
|
|
|
|
// collect all states of all workflows
|
|
|
|
// TODO: regard workflows only which are used by motions
|
|
|
|
$scope.states = [];
|
|
|
|
var workflows = Workflow.getAll();
|
|
|
|
angular.forEach(workflows, function (workflow) {
|
|
|
|
if (workflows.length > 1) {
|
2016-02-24 09:42:28 +01:00
|
|
|
var wf = {};
|
2016-01-29 14:52:25 +01:00
|
|
|
wf.name = workflow.name;
|
2016-08-12 15:01:41 +02:00
|
|
|
wf.workflowHeader = true;
|
2015-11-23 21:31:15 +01:00
|
|
|
$scope.states.push(wf);
|
|
|
|
}
|
|
|
|
angular.forEach(workflow.states, function (state) {
|
|
|
|
$scope.states.push(state);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-08-12 15:01:41 +02:00
|
|
|
// update state
|
|
|
|
$scope.updateState = function (motion, state_id) {
|
|
|
|
$http.put('/rest/motions/motion/' + motion.id + '/set_state/', {'state': state_id});
|
|
|
|
};
|
|
|
|
// reset state
|
|
|
|
$scope.reset_state = function (motion) {
|
|
|
|
$http.put('/rest/motions/motion/' + motion.id + '/set_state/', {});
|
2015-11-27 23:09:38 +01:00
|
|
|
};
|
2016-08-12 15:01:41 +02:00
|
|
|
|
|
|
|
$scope.has_tag = function (motion, tag) {
|
|
|
|
return _.indexOf(motion.tags_id, tag.id) > -1;
|
2016-02-08 21:19:13 +01:00
|
|
|
};
|
2016-08-12 15:01:41 +02:00
|
|
|
|
|
|
|
// Use this methon instead of Motion.save(), because otherwise
|
|
|
|
// you have to provide always a title and a text
|
|
|
|
var save = function (motion) {
|
2015-11-23 21:31:15 +01:00
|
|
|
motion.title = motion.getTitle(-1);
|
|
|
|
motion.text = motion.getText(-1);
|
|
|
|
motion.reason = motion.getReason(-1);
|
2016-08-12 15:01:41 +02:00
|
|
|
Motion.save(motion);
|
|
|
|
};
|
|
|
|
$scope.toggle_tag = function (motion, tag) {
|
|
|
|
if ($scope.has_tag(motion, tag)) {
|
|
|
|
// remove
|
|
|
|
motion.tags_id = _.filter(motion.tags_id, function (tag_id){
|
|
|
|
return tag_id != tag.id;
|
2015-11-23 21:31:15 +01:00
|
|
|
});
|
2016-08-12 15:01:41 +02:00
|
|
|
} else {
|
|
|
|
motion.tags_id.push(tag.id);
|
|
|
|
}
|
|
|
|
save(motion);
|
|
|
|
};
|
|
|
|
$scope.toggle_category = function (motion, category) {
|
|
|
|
if (motion.category_id == category.id) {
|
|
|
|
motion.category_id = null;
|
|
|
|
} else {
|
|
|
|
motion.category_id = category.id;
|
|
|
|
}
|
|
|
|
save(motion);
|
|
|
|
};
|
|
|
|
|
|
|
|
// open new/edit dialog
|
|
|
|
$scope.openDialog = function (motion) {
|
|
|
|
ngDialog.open(MotionForm.getDialog(motion));
|
|
|
|
};
|
|
|
|
|
2016-09-20 16:29:42 +02:00
|
|
|
|
|
|
|
// Export as a pdf file
|
|
|
|
$scope.pdf_export = function() {
|
|
|
|
|
|
|
|
var filename = gettextCatalog.getString("Motions") + ".pdf";
|
|
|
|
var image_sources = [];
|
|
|
|
|
|
|
|
//save the arrays of the filtered motions to an array
|
|
|
|
angular.forEach($scope.motionsFiltered, function (motion) {
|
2016-09-26 18:53:59 +02:00
|
|
|
var content = HTMLValidizer.validize(motion.getText($scope.version)) + HTMLValidizer.validize(motion.getReason($scope.version));
|
2016-09-20 16:29:42 +02:00
|
|
|
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)).success(function(data) {
|
|
|
|
var converter = PdfMakeConverter.createInstance(data.images, data.fonts, pdfMake);
|
|
|
|
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, data.defaultFont);
|
|
|
|
pdfMake.createPdf(documentProvider.getDocument()).download(filename);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2016-09-13 11:54:30 +02:00
|
|
|
// Export as a csv file
|
2016-08-12 15:01:41 +02:00
|
|
|
$scope.csv_export = function () {
|
2016-09-13 11:54:30 +02:00
|
|
|
var element = document.getElementById('downloadLinkCSV');
|
2016-08-12 15:01:41 +02:00
|
|
|
var csvRows = [
|
|
|
|
['identifier', 'title', 'text', 'reason', 'submitter', 'category', 'origin'],
|
|
|
|
];
|
|
|
|
angular.forEach($scope.motionsFiltered, function (motion) {
|
|
|
|
var row = [];
|
|
|
|
row.push('"' + motion.identifier + '"');
|
|
|
|
row.push('"' + motion.getTitle() + '"');
|
|
|
|
row.push('"' + motion.getText() + '"');
|
|
|
|
row.push('"' + motion.getReason() + '"');
|
|
|
|
row.push('"' + motion.submitters[0].get_full_name() + '"');
|
|
|
|
var category = motion.category ? motion.category.name : '';
|
|
|
|
row.push('"' + category + '"');
|
|
|
|
row.push('"' + motion.origin + '"');
|
|
|
|
csvRows.push(row);
|
|
|
|
});
|
|
|
|
|
|
|
|
var csvString = csvRows.join("%0A");
|
|
|
|
element.href = 'data:text/csv;charset=utf-8,' + csvString;
|
|
|
|
element.download = 'motions-export.csv';
|
|
|
|
element.target = '_blank';
|
2015-11-23 21:31:15 +01:00
|
|
|
};
|
2016-09-13 11:54:30 +02:00
|
|
|
// Export as docx file
|
|
|
|
$scope.docx_export = function () {
|
|
|
|
MotionDocxExport.export($scope.motionsFiltered, $scope.categories);
|
|
|
|
};
|
2015-11-23 21:31:15 +01:00
|
|
|
|
|
|
|
// *** delete mode functions ***
|
|
|
|
$scope.isDeleteMode = false;
|
2016-08-12 15:01:41 +02:00
|
|
|
// check all checkboxes from filtered motions
|
2015-11-23 21:31:15 +01:00
|
|
|
$scope.checkAll = function () {
|
2016-08-12 15:01:41 +02:00
|
|
|
$scope.selectedAll = !$scope.selectedAll;
|
|
|
|
angular.forEach($scope.motionsFiltered, function (motion) {
|
2015-11-23 21:31:15 +01:00
|
|
|
motion.selected = $scope.selectedAll;
|
|
|
|
});
|
|
|
|
};
|
|
|
|
// uncheck all checkboxes if isDeleteMode is closed
|
|
|
|
$scope.uncheckAll = function () {
|
|
|
|
if (!$scope.isDeleteMode) {
|
|
|
|
$scope.selectedAll = false;
|
|
|
|
angular.forEach($scope.motions, function (motion) {
|
|
|
|
motion.selected = false;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
// delete selected motions
|
2015-11-27 23:09:38 +01:00
|
|
|
$scope.deleteMultiple = function () {
|
2016-08-12 15:01:41 +02:00
|
|
|
angular.forEach($scope.motionsFiltered, function (motion) {
|
2015-11-23 21:31:15 +01:00
|
|
|
if (motion.selected)
|
|
|
|
Motion.destroy(motion.id);
|
|
|
|
});
|
|
|
|
$scope.isDeleteMode = false;
|
|
|
|
$scope.uncheckAll();
|
|
|
|
};
|
|
|
|
// delete single motion
|
2015-11-27 23:09:38 +01:00
|
|
|
$scope.delete = function (motion) {
|
2015-11-23 21:31:15 +01:00
|
|
|
Motion.destroy(motion.id);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
])
|
|
|
|
|
|
|
|
.controller('MotionDetailCtrl', [
|
|
|
|
'$scope',
|
|
|
|
'$http',
|
2016-09-30 17:19:27 +02:00
|
|
|
'operator',
|
2015-11-27 23:09:38 +01:00
|
|
|
'ngDialog',
|
2016-01-10 13:47:59 +01:00
|
|
|
'MotionForm',
|
2015-11-23 21:31:15 +01:00
|
|
|
'Motion',
|
|
|
|
'Category',
|
|
|
|
'Mediafile',
|
|
|
|
'Tag',
|
|
|
|
'User',
|
|
|
|
'Workflow',
|
2016-08-20 10:07:56 +02:00
|
|
|
'Config',
|
2015-11-23 21:31:15 +01:00
|
|
|
'motion',
|
2016-08-19 14:10:30 +02:00
|
|
|
'MotionContentProvider',
|
2016-08-28 22:57:05 +02:00
|
|
|
'PollContentProvider',
|
2016-08-19 14:10:30 +02:00
|
|
|
'PdfMakeConverter',
|
|
|
|
'PdfMakeDocumentProvider',
|
2016-09-07 21:42:53 +02:00
|
|
|
'MotionInlineEditing',
|
2016-08-19 14:44:58 +02:00
|
|
|
'gettextCatalog',
|
2016-09-05 16:23:44 +02:00
|
|
|
'Projector',
|
2016-09-26 18:53:59 +02:00
|
|
|
'HTMLValidizer',
|
2016-09-12 11:05:34 +02:00
|
|
|
'ProjectionDefault',
|
2016-09-30 17:19:27 +02:00
|
|
|
function($scope, $http, operator, ngDialog, MotionForm, Motion, Category, Mediafile, Tag, User, Workflow, Config,
|
2016-09-12 11:05:34 +02:00
|
|
|
motion, MotionContentProvider, PollContentProvider, PdfMakeConverter, PdfMakeDocumentProvider,
|
|
|
|
MotionInlineEditing, gettextCatalog, Projector, HTMLValidizer, ProjectionDefault) {
|
2015-11-23 21:31:15 +01:00
|
|
|
Motion.bindOne(motion.id, $scope, 'motion');
|
|
|
|
Category.bindAll({}, $scope, 'categories');
|
|
|
|
Mediafile.bindAll({}, $scope, 'mediafiles');
|
|
|
|
Tag.bindAll({}, $scope, 'tags');
|
|
|
|
User.bindAll({}, $scope, 'users');
|
|
|
|
Workflow.bindAll({}, $scope, 'workflows');
|
|
|
|
Motion.loadRelations(motion, 'agenda_item');
|
2016-09-12 11:05:34 +02:00
|
|
|
$scope.$watch(function () {
|
|
|
|
return Projector.lastModified();
|
|
|
|
}, function () {
|
|
|
|
$scope.defaultProjectorId = ProjectionDefault.filter({name: 'motions'})[0].projector_id;
|
|
|
|
});
|
2016-01-18 00:15:28 +01:00
|
|
|
$scope.version = motion.active_version;
|
2015-11-23 21:31:15 +01:00
|
|
|
$scope.isCollapsed = true;
|
2016-09-09 15:16:56 +02:00
|
|
|
$scope.commentsFields = Config.get('motions_comments').value;
|
2016-08-20 10:07:56 +02:00
|
|
|
$scope.lineNumberMode = Config.get('motions_default_line_numbering').value;
|
2016-08-19 21:03:14 +02:00
|
|
|
if (motion.parent_id) {
|
|
|
|
Motion.bindOne(motion.parent_id, $scope, 'parent');
|
|
|
|
}
|
|
|
|
$scope.amendments = Motion.filter({parent_id: motion.id});
|
2015-11-23 21:31:15 +01:00
|
|
|
|
2016-09-05 16:23:44 +02:00
|
|
|
$scope.highlight = 0;
|
|
|
|
$scope.linesForProjector = false;
|
|
|
|
// Set 0 for disable highlighting on projector
|
|
|
|
var setHighlightOnProjector = function (line) {
|
|
|
|
var elements = _.map(Projector.get(1).elements, function(element) { return element; });
|
|
|
|
elements.forEach(function (element) {
|
|
|
|
if (element.name == 'motions/motion') {
|
|
|
|
var data = {};
|
|
|
|
data[element.uuid] = {
|
|
|
|
highlightAndScroll: line,
|
|
|
|
};
|
|
|
|
$http.post('/rest/core/projector/1/update_elements/', data);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
$scope.scrollToAndHighlight = function (line) {
|
|
|
|
$scope.highlight = line;
|
|
|
|
var lineElement = document.getElementsByName('L' + line);
|
|
|
|
if (lineElement[0]) {
|
|
|
|
// Scroll local
|
|
|
|
$('html, body').animate({
|
|
|
|
scrollTop: lineElement[0].getBoundingClientRect().top
|
|
|
|
}, 1000);
|
|
|
|
}
|
|
|
|
// set highlight and scroll on Projector
|
|
|
|
setHighlightOnProjector($scope.linesForProjector ? line : 0);
|
|
|
|
};
|
|
|
|
$scope.toggleLinesForProjector = function () {
|
|
|
|
$scope.linesForProjector = !$scope.linesForProjector;
|
|
|
|
setHighlightOnProjector($scope.linesForProjector ? $scope.highlight : 0);
|
|
|
|
};
|
|
|
|
|
2016-09-06 11:52:27 +02:00
|
|
|
$scope.makePDF = function() {
|
2016-09-26 18:53:59 +02:00
|
|
|
var content = HTMLValidizer.validize(motion.getText($scope.version)) + HTMLValidizer.validize(motion.getReason($scope.version));
|
2016-09-20 16:29:42 +02:00
|
|
|
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)).success(function(data) {
|
|
|
|
var converter = PdfMakeConverter.createInstance(data.images, data.fonts, pdfMake);
|
|
|
|
var motionContentProvider = MotionContentProvider.createInstance(converter, motion, $scope, User, $http);
|
|
|
|
var documentProvider = PdfMakeDocumentProvider.createInstance(motionContentProvider, data.defaultFont);
|
|
|
|
var filename = gettextCatalog.getString("Motion") + "-" + motion.identifier + ".pdf";
|
|
|
|
pdfMake.createPdf(documentProvider.getDocument()).download(filename);
|
|
|
|
});
|
|
|
|
|
2016-08-19 14:10:30 +02:00
|
|
|
};
|
|
|
|
|
2016-08-28 22:57:05 +02:00
|
|
|
//make PDF for polls
|
|
|
|
$scope.makePollPDF = function() {
|
|
|
|
var id = motion.identifier.replace(" ", ""),
|
|
|
|
title = motion.getTitle($scope.version),
|
|
|
|
filename = gettextCatalog.getString("Motion") + "-" + id + "-" + gettextCatalog.getString("ballot-paper") + ".pdf",
|
|
|
|
content = PollContentProvider.createInstance(title, id, gettextCatalog);
|
|
|
|
pdfMake.createPdf(content).download(filename);
|
|
|
|
};
|
|
|
|
|
2015-11-27 23:09:38 +01:00
|
|
|
// open edit dialog
|
2016-01-10 13:47:59 +01:00
|
|
|
$scope.openDialog = function (motion) {
|
2016-08-20 10:07:56 +02:00
|
|
|
if ($scope.inlineEditing.active) {
|
|
|
|
$scope.disableInlineEditing();
|
|
|
|
}
|
2016-01-10 13:47:59 +01:00
|
|
|
ngDialog.open(MotionForm.getDialog(motion));
|
2015-11-27 23:09:38 +01:00
|
|
|
};
|
2016-01-14 22:43:49 +01:00
|
|
|
// support
|
2015-11-23 21:31:15 +01:00
|
|
|
$scope.support = function () {
|
|
|
|
$http.post('/rest/motions/motion/' + motion.id + '/support/');
|
2016-02-24 09:42:28 +01:00
|
|
|
};
|
2016-01-14 22:43:49 +01:00
|
|
|
// unsupport
|
2015-11-23 21:31:15 +01:00
|
|
|
$scope.unsupport = function () {
|
|
|
|
$http.delete('/rest/motions/motion/' + motion.id + '/support/');
|
2016-02-24 09:42:28 +01:00
|
|
|
};
|
2016-08-19 21:03:14 +02:00
|
|
|
// open dialog for new amendment
|
|
|
|
$scope.newAmendment = function () {
|
|
|
|
var dialog = MotionForm.getDialog();
|
|
|
|
if (dialog.scope === undefined) {
|
|
|
|
dialog.scope = {};
|
|
|
|
}
|
|
|
|
dialog.scope = $scope;
|
|
|
|
ngDialog.open(dialog);
|
|
|
|
};
|
2016-01-14 22:43:49 +01:00
|
|
|
// update state
|
2016-01-10 13:47:59 +01:00
|
|
|
$scope.updateState = function (state_id) {
|
|
|
|
$http.put('/rest/motions/motion/' + motion.id + '/set_state/', {'state': state_id});
|
2016-02-24 09:42:28 +01:00
|
|
|
};
|
2016-01-14 22:43:49 +01:00
|
|
|
// reset state
|
2016-09-03 21:43:11 +02:00
|
|
|
$scope.reset_state = function () {
|
2015-11-23 21:31:15 +01:00
|
|
|
$http.put('/rest/motions/motion/' + motion.id + '/set_state/', {});
|
2016-02-24 09:42:28 +01:00
|
|
|
};
|
2016-09-03 21:43:11 +02:00
|
|
|
// update recommendation
|
|
|
|
$scope.updateRecommendation = function (recommendation_id) {
|
|
|
|
$http.put('/rest/motions/motion/' + motion.id + '/set_recommendation/', {'recommendation': recommendation_id});
|
|
|
|
};
|
|
|
|
// reset state
|
|
|
|
$scope.resetRecommendation = function () {
|
|
|
|
$http.put('/rest/motions/motion/' + motion.id + '/set_recommendation/', {});
|
|
|
|
};
|
2016-01-14 22:43:49 +01:00
|
|
|
// create poll
|
2015-11-23 21:31:15 +01:00
|
|
|
$scope.create_poll = function () {
|
2016-01-14 22:43:49 +01:00
|
|
|
$http.post('/rest/motions/motion/' + motion.id + '/create_poll/', {});
|
2016-02-24 09:42:28 +01:00
|
|
|
};
|
2016-01-14 22:43:49 +01:00
|
|
|
// open poll update dialog
|
|
|
|
$scope.openPollDialog = function (poll, voteNumber) {
|
|
|
|
ngDialog.open({
|
|
|
|
template: 'static/templates/motions/motionpoll-form.html',
|
|
|
|
controller: 'MotionPollUpdateCtrl',
|
|
|
|
className: 'ngdialog-theme-default',
|
|
|
|
closeByEscape: false,
|
|
|
|
closeByDocument: false,
|
|
|
|
resolve: {
|
|
|
|
motionpoll: function (MotionPoll) {
|
|
|
|
return MotionPoll.find(poll.id);
|
|
|
|
},
|
|
|
|
voteNumber: function () {
|
|
|
|
return voteNumber;
|
|
|
|
}
|
2015-11-23 21:31:15 +01:00
|
|
|
}
|
|
|
|
});
|
2016-01-14 22:43:49 +01:00
|
|
|
};
|
|
|
|
// delete poll
|
|
|
|
$scope.delete_poll = function (poll) {
|
|
|
|
poll.DSDestroy();
|
2016-02-24 09:42:28 +01:00
|
|
|
};
|
2016-01-18 00:15:28 +01:00
|
|
|
// show specific version
|
|
|
|
$scope.showVersion = function (version) {
|
|
|
|
$scope.version = version.id;
|
2016-09-07 21:42:53 +02:00
|
|
|
$scope.inlineEditing.setVersion(motion, version.id);
|
2016-02-24 09:42:28 +01:00
|
|
|
};
|
2016-01-18 00:15:28 +01:00
|
|
|
// permit specific version
|
|
|
|
$scope.permitVersion = function (version) {
|
|
|
|
$http.put('/rest/motions/motion/' + motion.id + '/manage_version/',
|
|
|
|
{'version_number': version.version_number})
|
|
|
|
.then(function(success) {
|
2016-08-20 10:07:56 +02:00
|
|
|
$scope.showVersion(version);
|
2016-01-18 00:15:28 +01:00
|
|
|
});
|
2016-02-24 09:42:28 +01:00
|
|
|
};
|
2016-01-18 00:15:28 +01:00
|
|
|
// delete specific version
|
|
|
|
$scope.deleteVersion = function (version) {
|
|
|
|
$http.delete('/rest/motions/motion/' + motion.id + '/manage_version/',
|
|
|
|
{headers: {'Content-Type': 'application/json'},
|
|
|
|
data: JSON.stringify({version_number: version.version_number})})
|
|
|
|
.then(function(success) {
|
2016-08-20 10:07:56 +02:00
|
|
|
$scope.showVersion(motion.active_version);
|
2016-01-18 00:15:28 +01:00
|
|
|
});
|
2016-02-24 09:42:28 +01:00
|
|
|
};
|
2016-09-30 17:19:27 +02:00
|
|
|
// check if user is allowed to see at least one comment field
|
|
|
|
$scope.isAllowedToSeeCommentField = function () {
|
|
|
|
var isAllowed = false;
|
|
|
|
if ($scope.commentsFields.length > 0) {
|
|
|
|
isAllowed = operator.hasPerms('motions.can_see_and_manage_comments') || _.find(
|
|
|
|
$scope.commentsFields,
|
|
|
|
function(field) {
|
|
|
|
return field.public;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return Boolean(isAllowed);
|
|
|
|
};
|
2016-08-20 10:07:56 +02:00
|
|
|
|
|
|
|
// Inline editing functions
|
2016-09-07 21:42:53 +02:00
|
|
|
$scope.inlineEditing = MotionInlineEditing;
|
|
|
|
$scope.inlineEditing.init($scope, motion);
|
2015-11-23 21:31:15 +01:00
|
|
|
}
|
|
|
|
])
|
|
|
|
|
|
|
|
.controller('MotionCreateCtrl', [
|
|
|
|
'$scope',
|
2016-08-19 21:03:14 +02:00
|
|
|
'$state',
|
2015-11-23 21:31:15 +01:00
|
|
|
'gettext',
|
2016-09-05 23:13:07 +02:00
|
|
|
'gettextCatalog',
|
2016-09-08 09:44:47 +02:00
|
|
|
'operator',
|
2015-11-23 21:31:15 +01:00
|
|
|
'Motion',
|
2016-01-10 13:47:59 +01:00
|
|
|
'MotionForm',
|
2015-11-23 21:31:15 +01:00
|
|
|
'Category',
|
|
|
|
'Config',
|
|
|
|
'Mediafile',
|
|
|
|
'Tag',
|
|
|
|
'User',
|
|
|
|
'Workflow',
|
2016-01-22 23:19:41 +01:00
|
|
|
'Agenda',
|
2016-07-26 16:44:22 +02:00
|
|
|
'AgendaUpdate',
|
2016-09-08 09:44:47 +02:00
|
|
|
function($scope, $state, gettext, gettextCatalog, operator, Motion, MotionForm, Category, Config, Mediafile, Tag, User, Workflow, Agenda, AgendaUpdate) {
|
2015-11-23 21:31:15 +01:00
|
|
|
Category.bindAll({}, $scope, 'categories');
|
|
|
|
Mediafile.bindAll({}, $scope, 'mediafiles');
|
|
|
|
Tag.bindAll({}, $scope, 'tags');
|
|
|
|
User.bindAll({}, $scope, 'users');
|
|
|
|
Workflow.bindAll({}, $scope, 'workflows');
|
|
|
|
|
2015-12-11 23:33:55 +01:00
|
|
|
$scope.model = {};
|
2016-08-19 21:03:14 +02:00
|
|
|
|
|
|
|
// Check whether this is a new amendment.
|
|
|
|
var isAmendment = $scope.$parent.motion && $scope.$parent.motion.id;
|
|
|
|
|
|
|
|
// Set default values for create form
|
2015-12-11 23:33:55 +01:00
|
|
|
// ... set preamble config value as text
|
2016-09-05 23:13:07 +02:00
|
|
|
$scope.model.text = gettextCatalog.getString(Config.get('motions_preamble').value);
|
2016-08-19 21:03:14 +02:00
|
|
|
// ... for amendments add parent_id
|
|
|
|
if (isAmendment) {
|
|
|
|
if (Config.get('motions_amendments_apply_title_text').value) {
|
2016-08-25 13:39:00 +02:00
|
|
|
$scope.model.title = $scope.$parent.motion.getTitle();
|
2016-08-19 21:03:14 +02:00
|
|
|
$scope.model.text = $scope.$parent.motion.getText();
|
|
|
|
}
|
|
|
|
$scope.model.parent_id = $scope.$parent.motion.id;
|
|
|
|
Motion.bindOne($scope.model.parent_id, $scope, 'parent');
|
|
|
|
}
|
2015-12-11 23:33:55 +01:00
|
|
|
// ... preselect default workflow
|
|
|
|
$scope.model.workflow_id = Config.get('motions_workflow').value;
|
2015-11-23 21:31:15 +01:00
|
|
|
// get all form fields
|
2016-01-10 13:47:59 +01:00
|
|
|
$scope.formFields = MotionForm.getFormFields();
|
2016-08-19 21:03:14 +02:00
|
|
|
|
2015-11-27 23:09:38 +01:00
|
|
|
// save motion
|
2015-11-23 21:31:15 +01:00
|
|
|
$scope.save = function (motion) {
|
|
|
|
Motion.create(motion).then(
|
|
|
|
function(success) {
|
2016-09-08 09:44:47 +02:00
|
|
|
// change agenda item only if user has the permission to do that
|
|
|
|
if (operator.hasPerms('agenda.can_manage')) {
|
|
|
|
// type: Value 1 means a non hidden agenda item, value 2 means a hidden agenda item,
|
|
|
|
// see openslides.agenda.models.Item.ITEM_TYPE.
|
|
|
|
var changes = [{key: 'type', value: (motion.showAsAgendaItem ? 1 : 2)},
|
|
|
|
{key: 'parent_id', value: motion.agenda_parent_item_id}];
|
|
|
|
AgendaUpdate.saveChanges(success.agenda_item_id, changes);
|
|
|
|
}
|
2016-08-19 21:03:14 +02:00
|
|
|
if (isAmendment) {
|
|
|
|
$state.go('motions.motion.detail', {id: success.id});
|
|
|
|
}
|
2015-11-27 23:09:38 +01:00
|
|
|
$scope.closeThisDialog();
|
2015-11-23 21:31:15 +01:00
|
|
|
}
|
|
|
|
);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
])
|
|
|
|
|
|
|
|
.controller('MotionUpdateCtrl', [
|
|
|
|
'$scope',
|
|
|
|
'Motion',
|
|
|
|
'Category',
|
|
|
|
'Config',
|
|
|
|
'Mediafile',
|
2016-01-10 13:47:59 +01:00
|
|
|
'MotionForm',
|
2015-11-23 21:31:15 +01:00
|
|
|
'Tag',
|
|
|
|
'User',
|
|
|
|
'Workflow',
|
2016-01-22 23:19:41 +01:00
|
|
|
'Agenda',
|
2016-07-26 16:44:22 +02:00
|
|
|
'AgendaUpdate',
|
2015-11-23 21:31:15 +01:00
|
|
|
'motion',
|
2016-07-26 16:44:22 +02:00
|
|
|
function($scope, Motion, Category, Config, Mediafile, MotionForm, Tag, User, Workflow, Agenda, AgendaUpdate, motion) {
|
2015-11-23 21:31:15 +01:00
|
|
|
Category.bindAll({}, $scope, 'categories');
|
|
|
|
Mediafile.bindAll({}, $scope, 'mediafiles');
|
|
|
|
Tag.bindAll({}, $scope, 'tags');
|
|
|
|
User.bindAll({}, $scope, 'users');
|
|
|
|
Workflow.bindAll({}, $scope, 'workflows');
|
2016-01-16 14:19:00 +01:00
|
|
|
$scope.alert = {};
|
2015-11-23 21:31:15 +01:00
|
|
|
|
2016-01-16 14:19:00 +01:00
|
|
|
// set initial values for form model by create deep copy of motion object
|
|
|
|
// so list/detail view is not updated while editing
|
|
|
|
$scope.model = angular.copy(motion);
|
2015-11-23 21:31:15 +01:00
|
|
|
$scope.model.more = false;
|
2016-01-16 14:19:00 +01:00
|
|
|
|
2015-11-23 21:31:15 +01:00
|
|
|
// get all form fields
|
2016-01-10 13:47:59 +01:00
|
|
|
$scope.formFields = MotionForm.getFormFields();
|
2015-11-23 21:31:15 +01:00
|
|
|
// override default values for update form
|
|
|
|
for (var i = 0; i < $scope.formFields.length; i++) {
|
|
|
|
if ($scope.formFields[i].key == "identifier") {
|
|
|
|
// show identifier field
|
|
|
|
$scope.formFields[i].hide = false;
|
|
|
|
}
|
|
|
|
if ($scope.formFields[i].key == "title") {
|
|
|
|
// get title of latest version
|
|
|
|
$scope.formFields[i].defaultValue = motion.getTitle(-1);
|
|
|
|
}
|
|
|
|
if ($scope.formFields[i].key == "text") {
|
|
|
|
// get text of latest version
|
|
|
|
$scope.formFields[i].defaultValue = motion.getText(-1);
|
|
|
|
}
|
|
|
|
if ($scope.formFields[i].key == "reason") {
|
|
|
|
// get reason of latest version
|
|
|
|
$scope.formFields[i].defaultValue = motion.getReason(-1);
|
|
|
|
}
|
2016-01-17 22:32:12 +01:00
|
|
|
if ($scope.formFields[i].key == "disable_versioning" &&
|
|
|
|
Config.get('motions_allow_disable_versioning')) {
|
|
|
|
// check current state if versioning is active
|
|
|
|
if (motion.state.versioning) {
|
|
|
|
$scope.formFields[i].hide = false;
|
|
|
|
}
|
|
|
|
}
|
2016-01-22 23:19:41 +01:00
|
|
|
if ($scope.formFields[i].key == "showAsAgendaItem") {
|
|
|
|
// get state from agenda item (hidden/internal or agenda item)
|
|
|
|
$scope.formFields[i].defaultValue = !motion.agenda_item.is_hidden;
|
|
|
|
}
|
2015-11-23 21:31:15 +01:00
|
|
|
if ($scope.formFields[i].key == "workflow_id") {
|
|
|
|
// get saved workflow id from state
|
|
|
|
$scope.formFields[i].defaultValue = motion.state.workflow_id;
|
|
|
|
}
|
2016-07-26 16:44:22 +02:00
|
|
|
if ($scope.formFields[i].key == "agenda_parent_item_id") {
|
|
|
|
// get current parent_id of the agenda item
|
|
|
|
$scope.formFields[i].defaultValue = motion.agenda_item.parent_id;
|
|
|
|
}
|
2016-01-10 13:47:59 +01:00
|
|
|
}
|
2015-11-23 21:31:15 +01:00
|
|
|
|
2015-11-27 23:09:38 +01:00
|
|
|
// save motion
|
|
|
|
$scope.save = function (motion) {
|
2016-01-16 14:19:00 +01:00
|
|
|
// inject the changed motion (copy) object back into DS store
|
|
|
|
Motion.inject(motion);
|
|
|
|
// save change motion object on server
|
2016-01-14 23:44:19 +01:00
|
|
|
Motion.save(motion, { method: 'PATCH' }).then(
|
2015-11-27 23:09:38 +01:00
|
|
|
function(success) {
|
2016-07-26 16:44:22 +02:00
|
|
|
Agenda.find(success.agenda_item_id).then(function(item) {
|
|
|
|
// type: Value 1 means a non hidden agenda item, value 2 means a hidden agenda item,
|
|
|
|
// see openslides.agenda.models.Item.ITEM_TYPE.
|
|
|
|
var changes = [{key: 'type', value: (motion.showAsAgendaItem ? 1 : 2)},
|
|
|
|
{key: 'parent_id', value: motion.agenda_parent_item_id}];
|
|
|
|
AgendaUpdate.saveChanges(success.agenda_item_id,changes);
|
|
|
|
$scope.closeThisDialog();
|
|
|
|
});
|
2016-01-16 14:19:00 +01:00
|
|
|
},
|
|
|
|
function (error) {
|
|
|
|
// save error: revert all changes by restore
|
|
|
|
// (refresh) original motion object from server
|
|
|
|
Motion.refresh(motion);
|
|
|
|
var message = '';
|
|
|
|
for (var e in error.data) {
|
|
|
|
message += e + ': ' + error.data[e] + ' ';
|
|
|
|
}
|
|
|
|
$scope.alert = {type: 'danger', msg: message, show: true};
|
2015-11-27 23:09:38 +01:00
|
|
|
}
|
|
|
|
);
|
2015-11-23 21:31:15 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
])
|
|
|
|
|
2016-01-14 22:43:49 +01:00
|
|
|
.controller('MotionPollUpdateCtrl', [
|
|
|
|
'$scope',
|
|
|
|
'gettextCatalog',
|
|
|
|
'MotionPoll',
|
|
|
|
'MotionPollForm',
|
|
|
|
'motionpoll',
|
|
|
|
'voteNumber',
|
2016-01-24 00:19:54 +01:00
|
|
|
function($scope, gettextCatalog, MotionPoll, MotionPollForm, motionpoll, voteNumber) {
|
2016-02-08 21:41:01 +01:00
|
|
|
// set initial values for form model by create deep copy of motionpoll object
|
|
|
|
// so detail view is not updated while editing poll
|
|
|
|
$scope.model = angular.copy(motionpoll);
|
2016-01-14 22:43:49 +01:00
|
|
|
$scope.voteNumber = voteNumber;
|
|
|
|
$scope.formFields = MotionPollForm.getFormFields();
|
|
|
|
$scope.alert = {};
|
|
|
|
|
|
|
|
// save motionpoll
|
|
|
|
$scope.save = function (poll) {
|
|
|
|
poll.DSUpdate({
|
|
|
|
motion_id: poll.motion_id,
|
|
|
|
votes: {"Yes": poll.yes, "No": poll.no, "Abstain": poll.abstain},
|
|
|
|
votesvalid: poll.votesvalid,
|
|
|
|
votesinvalid: poll.votesinvalid,
|
|
|
|
votescast: poll.votescast
|
|
|
|
})
|
|
|
|
.then(function(success) {
|
|
|
|
$scope.alert.show = false;
|
|
|
|
$scope.closeThisDialog();
|
|
|
|
})
|
|
|
|
.catch(function(error) {
|
|
|
|
var message = '';
|
|
|
|
for (var e in error.data) {
|
|
|
|
message += e + ': ' + error.data[e] + ' ';
|
|
|
|
}
|
|
|
|
$scope.alert = { type: 'danger', msg: message, show: true };
|
|
|
|
});
|
|
|
|
};
|
|
|
|
}
|
|
|
|
])
|
|
|
|
|
2015-11-28 19:59:58 +01:00
|
|
|
.controller('MotionImportCtrl', [
|
|
|
|
'$scope',
|
2016-08-19 15:08:15 +02:00
|
|
|
'$q',
|
2015-11-28 19:59:58 +01:00
|
|
|
'gettext',
|
|
|
|
'Category',
|
|
|
|
'Motion',
|
|
|
|
'User',
|
2016-08-19 15:08:15 +02:00
|
|
|
function($scope, $q, gettext, Category, Motion, User) {
|
2015-11-28 19:59:58 +01:00
|
|
|
// set initial data for csv import
|
2016-02-24 09:42:28 +01:00
|
|
|
$scope.motions = [];
|
2015-11-28 19:59:58 +01:00
|
|
|
$scope.separator = ',';
|
|
|
|
$scope.encoding = 'UTF-8';
|
|
|
|
$scope.encodingOptions = ['UTF-8', 'ISO-8859-1'];
|
2016-06-02 13:01:12 +02:00
|
|
|
$scope.accept = '.csv, .txt';
|
2015-11-28 19:59:58 +01:00
|
|
|
$scope.csv = {
|
|
|
|
content: null,
|
|
|
|
header: true,
|
|
|
|
headerVisible: false,
|
|
|
|
separator: $scope.separator,
|
|
|
|
separatorVisible: false,
|
|
|
|
encoding: $scope.encoding,
|
|
|
|
encodingVisible: false,
|
2016-06-02 13:01:12 +02:00
|
|
|
accept: $scope.accept,
|
2015-11-28 19:59:58 +01:00
|
|
|
result: null
|
|
|
|
};
|
|
|
|
// set csv file encoding
|
|
|
|
$scope.setEncoding = function () {
|
|
|
|
$scope.csv.encoding = $scope.encoding;
|
|
|
|
};
|
|
|
|
// set csv file encoding
|
|
|
|
$scope.setSeparator = function () {
|
|
|
|
$scope.csv.separator = $scope.separator;
|
|
|
|
};
|
|
|
|
// detect if csv file is loaded
|
|
|
|
$scope.$watch('csv.result', function () {
|
|
|
|
$scope.motions = [];
|
|
|
|
var quotionRe = /^"(.*)"$/;
|
|
|
|
angular.forEach($scope.csv.result, function (motion) {
|
|
|
|
if (motion.identifier) {
|
|
|
|
motion.identifier = motion.identifier.replace(quotionRe, '$1');
|
2016-02-24 09:42:28 +01:00
|
|
|
if (motion.identifier !== '') {
|
2015-11-28 19:59:58 +01:00
|
|
|
// All motion objects are already loaded via the resolve statement from ui-router.
|
|
|
|
var motions = Motion.getAll();
|
|
|
|
if (_.find(motions, function (item) {
|
|
|
|
return item.identifier == motion.identifier;
|
|
|
|
})) {
|
|
|
|
motion.importerror = true;
|
|
|
|
motion.identifier_error = gettext('Error: Identifier already exists.');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// title
|
|
|
|
if (motion.title) {
|
|
|
|
motion.title = motion.title.replace(quotionRe, '$1');
|
|
|
|
}
|
|
|
|
if (!motion.title) {
|
|
|
|
motion.importerror = true;
|
|
|
|
motion.title_error = gettext('Error: Title is required.');
|
|
|
|
}
|
|
|
|
// text
|
|
|
|
if (motion.text) {
|
|
|
|
motion.text = motion.text.replace(quotionRe, '$1');
|
|
|
|
}
|
|
|
|
if (!motion.text) {
|
|
|
|
motion.importerror = true;
|
|
|
|
motion.text_error = gettext('Error: Text is required.');
|
|
|
|
}
|
|
|
|
// reason
|
|
|
|
if (motion.reason) {
|
|
|
|
motion.reason = motion.reason.replace(quotionRe, '$1');
|
|
|
|
}
|
|
|
|
// submitter
|
|
|
|
if (motion.submitter) {
|
|
|
|
motion.submitter = motion.submitter.replace(quotionRe, '$1');
|
2016-02-24 09:42:28 +01:00
|
|
|
if (motion.submitter !== '') {
|
2015-11-28 19:59:58 +01:00
|
|
|
// All user objects are already loaded via the resolve statement from ui-router.
|
|
|
|
var users = User.getAll();
|
|
|
|
angular.forEach(users, function (user) {
|
|
|
|
if (user.short_name == motion.submitter) {
|
|
|
|
motion.submitters_id = [user.id];
|
|
|
|
motion.submitter = User.get(user.id).full_name;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2016-02-24 09:42:28 +01:00
|
|
|
if (motion.submitter && motion.submitter !== '' && !motion.submitters_id) {
|
2015-11-28 19:59:58 +01:00
|
|
|
motion.submitter_create = gettext('New participant will be created.');
|
|
|
|
}
|
|
|
|
// category
|
|
|
|
if (motion.category) {
|
|
|
|
motion.category = motion.category.replace(quotionRe, '$1');
|
2016-02-24 09:42:28 +01:00
|
|
|
if (motion.category !== '') {
|
2015-11-28 19:59:58 +01:00
|
|
|
// All categore objects are already loaded via the resolve statement from ui-router.
|
|
|
|
var categories = Category.getAll();
|
|
|
|
angular.forEach(categories, function (category) {
|
|
|
|
// search for existing category
|
|
|
|
if (category.name == motion.category) {
|
|
|
|
motion.category_id = category.id;
|
|
|
|
motion.category = Category.get(category.id).name;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2016-02-24 09:42:28 +01:00
|
|
|
if (motion.category && motion.category !== '' && !motion.category_id) {
|
2015-11-28 19:59:58 +01:00
|
|
|
motion.category_create = gettext('New category will be created.');
|
|
|
|
}
|
2016-07-13 14:45:40 +02:00
|
|
|
// origin
|
|
|
|
if (motion.origin) {
|
|
|
|
motion.origin = motion.origin.replace(quotionRe, '$1');
|
|
|
|
}
|
2015-11-28 19:59:58 +01:00
|
|
|
$scope.motions.push(motion);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-08-19 15:08:15 +02:00
|
|
|
// Counter for creations
|
|
|
|
$scope.usersCreated = 0;
|
|
|
|
$scope.categoriesCreated = 0;
|
|
|
|
|
2015-11-28 19:59:58 +01:00
|
|
|
// import from csv file
|
|
|
|
$scope.import = function () {
|
|
|
|
$scope.csvImporting = true;
|
2016-08-19 15:08:15 +02:00
|
|
|
|
|
|
|
// Reset counters
|
|
|
|
$scope.usersCreated = 0;
|
|
|
|
$scope.categoriesCreated = 0;
|
|
|
|
|
|
|
|
var importedUsers = [];
|
|
|
|
var importedCategories = [];
|
|
|
|
// collect users and categories
|
2016-08-19 21:03:14 +02:00
|
|
|
angular.forEach($scope.motions, function (motion) {
|
2015-11-28 19:59:58 +01:00
|
|
|
if (!motion.importerror) {
|
2016-08-19 15:08:15 +02:00
|
|
|
// collect user if not exists
|
2016-02-17 21:11:36 +01:00
|
|
|
if (!motion.submitters_id && motion.submitter) {
|
2015-11-28 19:59:58 +01:00
|
|
|
var index = motion.submitter.indexOf(' ');
|
|
|
|
var user = {
|
|
|
|
first_name: motion.submitter.substr(0, index),
|
|
|
|
last_name: motion.submitter.substr(index+1),
|
2016-08-19 15:08:15 +02:00
|
|
|
groups_id: []
|
2016-02-24 09:42:28 +01:00
|
|
|
};
|
2016-08-19 15:08:15 +02:00
|
|
|
importedUsers.push(user);
|
2015-11-28 19:59:58 +01:00
|
|
|
}
|
2016-08-19 15:08:15 +02:00
|
|
|
// collect category if not exists
|
2016-02-17 21:11:36 +01:00
|
|
|
if (!motion.category_id && motion.category) {
|
2015-11-28 19:59:58 +01:00
|
|
|
var category = {
|
|
|
|
name: motion.category,
|
|
|
|
prefix: motion.category.charAt(0)
|
2016-02-24 09:42:28 +01:00
|
|
|
};
|
2016-08-19 15:08:15 +02:00
|
|
|
importedCategories.push(category);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2016-09-22 12:34:53 +02:00
|
|
|
// unique users and categories
|
|
|
|
var importedUsersUnique = _.uniqWith(importedUsers, function (u1, u2) {
|
|
|
|
return u1.first_name == u2.first_name &&
|
|
|
|
u1.last_name == u2.last_name;
|
2016-08-19 15:08:15 +02:00
|
|
|
});
|
2016-09-22 12:34:53 +02:00
|
|
|
var importedCategoriesUnique = _.uniqWith(importedCategories, function (c1, c2) {
|
|
|
|
return c1.name == c2.name;
|
2016-08-19 15:08:15 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
// Promises for users and categories
|
|
|
|
var createPromises = [];
|
|
|
|
|
|
|
|
// create users and categories
|
|
|
|
importedUsersUnique.forEach(function (user) {
|
|
|
|
createPromises.push(User.create(user).then(
|
|
|
|
function (success) {
|
|
|
|
user.id = success.id;
|
|
|
|
$scope.usersCreated++;
|
|
|
|
}
|
|
|
|
));
|
|
|
|
});
|
|
|
|
importedCategoriesUnique.forEach(function (category) {
|
|
|
|
createPromises.push(Category.create(category).then(
|
|
|
|
function (success) {
|
|
|
|
category.id = success.id;
|
|
|
|
$scope.categoriesCreated++;
|
|
|
|
}
|
|
|
|
));
|
|
|
|
});
|
|
|
|
|
|
|
|
// wait for users and categories to create
|
|
|
|
$q.all(createPromises).then( function() {
|
|
|
|
angular.forEach($scope.motions, function (motion) {
|
|
|
|
if (!motion.importerror) {
|
|
|
|
// now, add user
|
|
|
|
if (!motion.submitters_id && motion.submitter) {
|
|
|
|
var index = motion.submitter.indexOf(' ');
|
|
|
|
var first_name = motion.submitter.substr(0, index);
|
|
|
|
var last_name = motion.submitter.substr(index+1);
|
2016-08-31 16:53:02 +02:00
|
|
|
|
2016-08-19 15:08:15 +02:00
|
|
|
// search for user, set id.
|
|
|
|
importedUsersUnique.forEach(function (user) {
|
|
|
|
if (user.first_name == first_name &&
|
|
|
|
user.last_name == last_name) {
|
|
|
|
motion.submitters_id = [user.id];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
// add category
|
|
|
|
if (!motion.category_id && motion.category) {
|
|
|
|
var name = motion.category;
|
2016-08-31 16:53:02 +02:00
|
|
|
|
2016-08-19 15:08:15 +02:00
|
|
|
// search for category, set id.
|
|
|
|
importedCategoriesUnique.forEach(function (category) {
|
|
|
|
if (category.name == name) {
|
|
|
|
motion.category_id = category.id;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// finally create motion
|
|
|
|
Motion.create(motion).then(
|
2015-11-28 19:59:58 +01:00
|
|
|
function(success) {
|
2016-08-19 15:08:15 +02:00
|
|
|
motion.imported = true;
|
2015-11-28 19:59:58 +01:00
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
2016-08-19 15:08:15 +02:00
|
|
|
});
|
2015-11-28 19:59:58 +01:00
|
|
|
});
|
|
|
|
$scope.csvimported = true;
|
|
|
|
};
|
|
|
|
$scope.clear = function () {
|
|
|
|
$scope.csv.result = null;
|
|
|
|
};
|
2015-12-30 21:56:03 +01:00
|
|
|
// download CSV example file
|
|
|
|
$scope.downloadCSVExample = function () {
|
|
|
|
var element = document.getElementById('downloadLink');
|
|
|
|
var csvRows = [
|
|
|
|
// column header line
|
2016-07-13 14:45:40 +02:00
|
|
|
['identifier', 'title', 'text', 'reason', 'submitter', 'category', 'origin'],
|
2015-12-30 21:56:03 +01:00
|
|
|
// example entries
|
2016-07-13 14:45:40 +02:00
|
|
|
['A1', 'Title 1', 'Text 1', 'Reason 1', 'Submitter A', 'Category A', 'Last Year Conference A'],
|
|
|
|
['B1', 'Title 2', 'Text 2', 'Reason 2', 'Submitter B', 'Category B', '' ],
|
|
|
|
['' , 'Title 3', 'Text 3', '' , '' , '' , '' ],
|
2015-12-30 21:56:03 +01:00
|
|
|
];
|
|
|
|
var csvString = csvRows.join("%0A");
|
|
|
|
element.href = 'data:text/csv;charset=utf-8,' + csvString;
|
|
|
|
element.download = 'motions-example.csv';
|
|
|
|
element.target = '_blank';
|
2016-02-24 09:42:28 +01:00
|
|
|
};
|
2015-11-28 19:59:58 +01:00
|
|
|
}
|
|
|
|
])
|
2015-11-23 21:31:15 +01:00
|
|
|
|
2016-01-14 22:43:49 +01:00
|
|
|
|
2016-01-17 21:16:04 +01:00
|
|
|
.controller('CategoryListCtrl', [
|
|
|
|
'$scope',
|
|
|
|
'Category',
|
|
|
|
function($scope, Category) {
|
|
|
|
Category.bindAll({}, $scope, 'categories');
|
2015-11-23 21:31:15 +01:00
|
|
|
|
2016-01-17 21:16:04 +01:00
|
|
|
// setup table sorting
|
|
|
|
$scope.sortColumn = 'name';
|
|
|
|
$scope.reverse = false;
|
|
|
|
// function to sort by clicked column
|
|
|
|
$scope.toggleSort = function ( column ) {
|
|
|
|
if ( $scope.sortColumn === column ) {
|
|
|
|
$scope.reverse = !$scope.reverse;
|
2015-11-23 21:31:15 +01:00
|
|
|
}
|
2016-01-17 21:16:04 +01:00
|
|
|
$scope.sortColumn = column;
|
|
|
|
};
|
|
|
|
|
|
|
|
// delete selected category
|
|
|
|
$scope.delete = function (category) {
|
|
|
|
Category.destroy(category.id);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
])
|
|
|
|
|
|
|
|
.controller('CategoryDetailCtrl', [
|
|
|
|
'$scope',
|
|
|
|
'Category',
|
|
|
|
'category',
|
|
|
|
function($scope, Category, category) {
|
|
|
|
Category.bindOne(category.id, $scope, 'category');
|
|
|
|
}
|
|
|
|
])
|
|
|
|
|
|
|
|
.controller('CategoryCreateCtrl', [
|
|
|
|
'$scope',
|
|
|
|
'$state',
|
|
|
|
'Category',
|
|
|
|
function($scope, $state, Category) {
|
|
|
|
$scope.category = {};
|
|
|
|
$scope.save = function (category) {
|
|
|
|
Category.create(category).then(
|
|
|
|
function(success) {
|
|
|
|
$state.go('motions.category.list');
|
|
|
|
}
|
|
|
|
);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
])
|
|
|
|
|
|
|
|
.controller('CategoryUpdateCtrl', [
|
|
|
|
'$scope',
|
|
|
|
'$state',
|
|
|
|
'Category',
|
|
|
|
'category',
|
|
|
|
function($scope, $state, Category, category) {
|
|
|
|
$scope.category = category;
|
|
|
|
$scope.save = function (category) {
|
|
|
|
Category.save(category).then(
|
|
|
|
function(success) {
|
|
|
|
$state.go('motions.category.list');
|
|
|
|
}
|
|
|
|
);
|
|
|
|
};
|
|
|
|
}
|
2016-06-09 16:12:13 +02:00
|
|
|
])
|
|
|
|
|
2016-08-17 12:26:27 +02:00
|
|
|
.controller('CategorySortCtrl', [
|
|
|
|
'$scope',
|
|
|
|
'$stateParams',
|
|
|
|
'$http',
|
|
|
|
'MotionList',
|
|
|
|
'Category',
|
|
|
|
'category',
|
|
|
|
'Motion',
|
|
|
|
'motions',
|
|
|
|
function($scope, $stateParams, $http, MotionList, Category, category, Motion, motions) {
|
|
|
|
Category.bindOne(category.id, $scope, 'category');
|
|
|
|
Motion.bindAll({}, $scope, 'motions');
|
|
|
|
$scope.filter = { category_id: category.id,
|
|
|
|
orderBy: 'identifier' };
|
|
|
|
|
|
|
|
$scope.$watch(
|
|
|
|
function () {
|
|
|
|
return Motion.lastModified();
|
|
|
|
},
|
|
|
|
function () {
|
2016-08-19 09:17:28 +02:00
|
|
|
$scope.items = MotionList.getList(Motion.filter($scope.filter));
|
2016-08-17 12:26:27 +02:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
$scope.alert = {};
|
|
|
|
// Numbers all motions in this category by the given order in $scope.items
|
|
|
|
$scope.numbering = function () {
|
|
|
|
// Create a list of all motion ids in the current order.
|
|
|
|
var sorted_motions = [];
|
|
|
|
$scope.items.forEach(function (item) {
|
|
|
|
sorted_motions.push(item.item.id);
|
|
|
|
});
|
|
|
|
|
|
|
|
// renumber them
|
|
|
|
$http.post('/rest/motions/category/' + $scope.category.id + '/numbering/',
|
|
|
|
{'motions': sorted_motions} )
|
|
|
|
.success(function(data) {
|
|
|
|
$scope.alert = { type: 'success', msg: data.detail, show: true };
|
|
|
|
})
|
|
|
|
.error(function(data) {
|
|
|
|
$scope.alert = { type: 'danger', msg: data.detail, show: true };
|
|
|
|
});
|
|
|
|
};
|
|
|
|
}
|
|
|
|
])
|
|
|
|
|
2016-06-09 16:12:13 +02:00
|
|
|
//mark all motions config strings for translation in javascript
|
|
|
|
.config([
|
|
|
|
'gettext',
|
|
|
|
function (gettext) {
|
|
|
|
gettext('Motions');
|
2016-08-29 16:08:27 +02:00
|
|
|
|
|
|
|
// subgroup General
|
|
|
|
gettext('General');
|
|
|
|
gettext('Workflow of new motions');
|
2016-06-09 16:12:13 +02:00
|
|
|
gettext('Identifier');
|
|
|
|
gettext('Numbered per category');
|
|
|
|
gettext('Serially numbered');
|
|
|
|
gettext('Set it manually');
|
|
|
|
gettext('Motion preamble');
|
2016-08-29 16:08:27 +02:00
|
|
|
gettext('The assembly may decide,');
|
|
|
|
gettext('Default line numbering');
|
|
|
|
/// Line numbering: Outside
|
|
|
|
gettext('Outside');
|
|
|
|
/// Line numbering: Inline
|
|
|
|
gettext('Inline');
|
|
|
|
/// Line numbering: None
|
|
|
|
gettext('None');
|
|
|
|
gettext('Line length');
|
|
|
|
gettext('The maximum number of characters per line. Relevant when line numbering is enabled. Min: 40');
|
2016-06-09 16:12:13 +02:00
|
|
|
gettext('Stop submitting new motions by non-staff users');
|
|
|
|
gettext('Allow to disable versioning');
|
2016-09-03 21:43:11 +02:00
|
|
|
gettext('Name of recommendation committee');
|
|
|
|
gettext('Use an empty value to disable the recommendation system.');
|
2016-08-29 16:08:27 +02:00
|
|
|
|
|
|
|
// subgroup Amendments
|
2016-06-09 16:12:13 +02:00
|
|
|
gettext('Amendments');
|
2016-08-29 16:08:27 +02:00
|
|
|
gettext('Activate amendments');
|
2016-06-09 16:12:13 +02:00
|
|
|
gettext('Prefix for the identifier for amendments');
|
2016-08-25 13:39:00 +02:00
|
|
|
gettext('Apply title and text for new amendments');
|
2016-08-29 16:08:27 +02:00
|
|
|
|
|
|
|
// subgroup Suppoerters
|
|
|
|
gettext('Supporters');
|
2016-06-09 16:12:13 +02:00
|
|
|
gettext('Number of (minimum) required supporters for a motion');
|
|
|
|
gettext('Choose 0 to disable the supporting system.');
|
2016-06-16 10:28:14 +02:00
|
|
|
gettext('Remove all supporters of a motion if a submitter edits his ' +
|
|
|
|
'motion in early state');
|
2016-08-29 16:08:27 +02:00
|
|
|
|
2016-10-04 12:00:11 +02:00
|
|
|
// subgroup Supporters
|
2016-09-26 15:33:40 +02:00
|
|
|
gettext('Comments');
|
|
|
|
gettext('Comment fields for motions');
|
|
|
|
gettext('Public');
|
|
|
|
gettext('Private');
|
|
|
|
|
2016-08-29 16:08:27 +02:00
|
|
|
// subgroup Voting and ballot papers
|
|
|
|
gettext('Voting and ballot papers');
|
|
|
|
gettext('The 100 % base of a voting result consists of');
|
2016-08-26 13:46:57 +02:00
|
|
|
gettext('Yes/No/Abstain');
|
|
|
|
gettext('Yes/No');
|
|
|
|
gettext('All valid ballots');
|
|
|
|
gettext('All casted ballots');
|
2016-08-29 16:08:27 +02:00
|
|
|
gettext('Disabled (no percents)');
|
|
|
|
gettext('Number of ballot papers (selection)');
|
|
|
|
gettext('Number of all delegates');
|
|
|
|
gettext('Number of all participants');
|
|
|
|
gettext('Use the following custom number');
|
|
|
|
gettext('Custom number of ballot papers');
|
|
|
|
|
|
|
|
// subgroup PDF
|
2016-09-26 12:44:23 +02:00
|
|
|
gettext('Title for PDF and DOCX documents (all motions)');
|
2016-09-26 15:33:40 +02:00
|
|
|
gettext('Preamble text for PDF and DOCX documents (all motions)');
|
2016-06-09 16:12:13 +02:00
|
|
|
}
|
2016-01-17 21:16:04 +01:00
|
|
|
]);
|
2015-11-23 21:31:15 +01:00
|
|
|
|
|
|
|
}());
|