2015-11-23 21:31:15 +01:00
|
|
|
(function () {
|
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
|
|
|
|
|
|
|
|
.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) {
|
|
|
|
return Motion.findAll();
|
|
|
|
},
|
|
|
|
categories: function(Category) {
|
|
|
|
return Category.findAll();
|
|
|
|
},
|
|
|
|
tags: function(Tag) {
|
|
|
|
return Tag.findAll();
|
|
|
|
},
|
|
|
|
users: function(User) {
|
|
|
|
return User.findAll();
|
|
|
|
},
|
|
|
|
workflows: function(Workflow) {
|
|
|
|
return Workflow.findAll();
|
|
|
|
}
|
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) {
|
|
|
|
return Motion.find($stateParams.id);
|
|
|
|
},
|
|
|
|
categories: function(Category) {
|
|
|
|
return Category.findAll();
|
|
|
|
},
|
|
|
|
users: function(User) {
|
|
|
|
return User.findAll();
|
|
|
|
},
|
|
|
|
mediafiles: function(Mediafile) {
|
|
|
|
return Mediafile.findAll();
|
|
|
|
},
|
|
|
|
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: {
|
|
|
|
motion: function() {return Motion.find($stateParams.id)}
|
|
|
|
},
|
|
|
|
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-01-17 21:16:04 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
])
|
2015-11-23 21:31:15 +01:00
|
|
|
|
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',
|
|
|
|
'Category',
|
|
|
|
'Config',
|
|
|
|
'Mediafile',
|
|
|
|
'Tag',
|
|
|
|
'User',
|
|
|
|
'Workflow',
|
2015-11-30 23:52:42 +01:00
|
|
|
function (gettextCatalog, operator, Category, Config, Mediafile, Tag, User, Workflow) {
|
2015-11-27 23:09:38 +01:00
|
|
|
return {
|
2016-01-10 13:47:59 +01:00
|
|
|
// ngDialog for motion form
|
|
|
|
getDialog: function (motion) {
|
|
|
|
if (motion) {
|
|
|
|
var resolve = {
|
|
|
|
motion: function(Motion) { return Motion.find(motion.id); }
|
|
|
|
};
|
|
|
|
}
|
|
|
|
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
|
|
|
|
}
|
|
|
|
},
|
|
|
|
// angular-formly fields for motion form
|
2015-11-27 23:09:38 +01:00
|
|
|
getFormFields: function () {
|
|
|
|
return [
|
|
|
|
{
|
|
|
|
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',
|
|
|
|
type: 'ui-select-multiple',
|
|
|
|
templateOptions: {
|
2015-11-30 23:52:42 +01:00
|
|
|
label: gettextCatalog.getString('Submitters'),
|
2015-11-27 23:09:38 +01:00
|
|
|
optionsAttr: 'bs-options',
|
|
|
|
options: User.getAll(),
|
|
|
|
ngOptions: 'option[to.valueProp] as option in to.options | filter: $select.search',
|
|
|
|
valueProp: 'id',
|
|
|
|
labelProp: 'full_name',
|
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',
|
|
|
|
type: 'textarea',
|
|
|
|
templateOptions: {
|
2015-11-30 23:52:42 +01:00
|
|
|
label: gettextCatalog.getString('Text'),
|
2015-11-27 23:09:38 +01:00
|
|
|
required: true
|
|
|
|
},
|
|
|
|
ngModelElAttrs: {'ckeditor': 'CKEditorOptions'}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
key: 'reason',
|
|
|
|
type: 'textarea',
|
|
|
|
templateOptions: {
|
2015-11-30 23:52:42 +01:00
|
|
|
label: gettextCatalog.getString('Reason')
|
2015-11-27 23:09:38 +01:00
|
|
|
},
|
|
|
|
ngModelElAttrs: {'ckeditor': 'CKEditorOptions'}
|
|
|
|
},
|
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
|
|
|
|
},
|
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',
|
|
|
|
type: 'ui-select-multiple',
|
|
|
|
templateOptions: {
|
2015-11-30 23:52:42 +01:00
|
|
|
label: gettextCatalog.getString('Attachment'),
|
2015-11-27 23:09:38 +01:00
|
|
|
optionsAttr: 'bs-options',
|
|
|
|
options: Mediafile.getAll(),
|
|
|
|
ngOptions: 'option[to.valueProp] as option in to.options | filter: $select.search',
|
|
|
|
valueProp: 'id',
|
|
|
|
labelProp: 'title_or_filename',
|
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',
|
|
|
|
type: 'ui-select-single',
|
|
|
|
templateOptions: {
|
2015-11-30 23:52:42 +01:00
|
|
|
label: gettextCatalog.getString('Category'),
|
2015-11-27 23:09:38 +01:00
|
|
|
optionsAttr: 'bs-options',
|
|
|
|
options: Category.getAll(),
|
|
|
|
ngOptions: 'option[to.valueProp] as option in to.options | filter: $select.search',
|
|
|
|
valueProp: 'id',
|
|
|
|
labelProp: 'name',
|
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'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
key: 'tags_id',
|
|
|
|
type: 'ui-select-multiple',
|
|
|
|
templateOptions: {
|
2015-11-30 23:52:42 +01:00
|
|
|
label: gettextCatalog.getString('Tags'),
|
2015-11-27 23:09:38 +01:00
|
|
|
optionsAttr: 'bs-options',
|
|
|
|
options: Tag.getAll(),
|
|
|
|
ngOptions: 'option[to.valueProp] as option in to.options | filter: $select.search',
|
|
|
|
valueProp: 'id',
|
|
|
|
labelProp: 'name',
|
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',
|
|
|
|
type: 'ui-select-multiple',
|
|
|
|
templateOptions: {
|
2015-11-30 23:52:42 +01:00
|
|
|
label: gettextCatalog.getString('Supporters'),
|
2015-11-27 23:09:38 +01:00
|
|
|
optionsAttr: 'bs-options',
|
|
|
|
options: User.getAll(),
|
|
|
|
ngOptions: 'option[to.valueProp] as option in to.options | filter: $select.search',
|
|
|
|
valueProp: 'id',
|
|
|
|
labelProp: 'full_name',
|
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'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
key: 'workflow_id',
|
|
|
|
type: 'ui-select-single',
|
|
|
|
templateOptions: {
|
2015-11-30 23:52:42 +01:00
|
|
|
label: gettextCatalog.getString('Workflow'),
|
2015-11-27 23:09:38 +01:00
|
|
|
optionsAttr: 'bs-options',
|
|
|
|
options: Workflow.getAll(),
|
|
|
|
ngOptions: 'option[to.valueProp] as option in to.options | filter: $select.search',
|
|
|
|
valueProp: 'id',
|
|
|
|
labelProp: 'name',
|
2015-12-11 20:04:05 +01:00
|
|
|
placeholder: gettextCatalog.getString('Select or search a workflow ...')
|
2015-11-27 23:09:38 +01:00
|
|
|
},
|
|
|
|
hideExpression: '!model.more',
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
])
|
|
|
|
|
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: {
|
|
|
|
label: gettextCatalog.getString('Valid votes'),
|
|
|
|
type: 'number'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
key: 'votesinvalid',
|
|
|
|
type: 'input',
|
|
|
|
templateOptions: {
|
|
|
|
label: gettextCatalog.getString('Invalid votes'),
|
|
|
|
type: 'number'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
key: 'votescast',
|
|
|
|
type: 'input',
|
|
|
|
templateOptions: {
|
|
|
|
label: gettextCatalog.getString('Votes cast'),
|
|
|
|
type: 'number'
|
|
|
|
}
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
])
|
|
|
|
|
2015-11-23 21:31:15 +01:00
|
|
|
.controller('MotionListCtrl', [
|
|
|
|
'$scope',
|
|
|
|
'$state',
|
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-01-10 13:47:59 +01:00
|
|
|
function($scope, $state, ngDialog, MotionForm, Motion, Category, Tag, Workflow, User) {
|
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');
|
|
|
|
$scope.alert = {};
|
|
|
|
|
|
|
|
// setup table sorting
|
|
|
|
$scope.sortColumn = 'identifier';
|
|
|
|
$scope.filterPresent = '';
|
|
|
|
$scope.reverse = false;
|
|
|
|
// 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-09 16:45:47 +01:00
|
|
|
$scope.getFilterString = function (motion) {
|
|
|
|
if (motion.category) {
|
|
|
|
var category = motion.category.name;
|
|
|
|
} else {
|
|
|
|
var category = ''
|
|
|
|
}
|
|
|
|
return [
|
|
|
|
motion.getTitle(),
|
|
|
|
motion.getText(),
|
|
|
|
_.map(motion.submitters, function (submitter) {return submitter.get_short_name()}).join(" "),
|
|
|
|
category].join(" ");
|
|
|
|
}
|
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) {
|
|
|
|
var wf = {}
|
|
|
|
wf.name = "# "+workflow.name;
|
|
|
|
$scope.states.push(wf);
|
|
|
|
}
|
|
|
|
angular.forEach(workflow.states, function (state) {
|
|
|
|
$scope.states.push(state);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-01-10 13:47:59 +01:00
|
|
|
// open new/edit dialog
|
|
|
|
$scope.openDialog = function (motion) {
|
|
|
|
ngDialog.open(MotionForm.getDialog(motion));
|
2015-11-27 23:09:38 +01:00
|
|
|
};
|
2015-11-23 21:31:15 +01:00
|
|
|
// save changed motion
|
2015-11-27 23:09:38 +01:00
|
|
|
$scope.save = function (motion) {
|
2015-11-23 21:31:15 +01:00
|
|
|
// get (unchanged) values from latest version for update method
|
|
|
|
motion.title = motion.getTitle(-1);
|
|
|
|
motion.text = motion.getText(-1);
|
|
|
|
motion.reason = motion.getReason(-1);
|
|
|
|
Motion.save(motion).then(
|
|
|
|
function(success) {
|
|
|
|
motion.quickEdit = false;
|
|
|
|
$scope.alert.show = false;
|
|
|
|
},
|
|
|
|
function(error){
|
|
|
|
var message = '';
|
|
|
|
for (var e in error.data) {
|
|
|
|
message += e + ': ' + error.data[e] + ' ';
|
|
|
|
}
|
|
|
|
$scope.alert = { type: 'danger', msg: message, show: true };
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
// *** delete mode functions ***
|
|
|
|
$scope.isDeleteMode = false;
|
|
|
|
// check all checkboxes
|
|
|
|
$scope.checkAll = function () {
|
|
|
|
angular.forEach($scope.motions, function (motion) {
|
|
|
|
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 () {
|
2015-11-23 21:31:15 +01:00
|
|
|
angular.forEach($scope.motions, function (motion) {
|
|
|
|
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',
|
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',
|
|
|
|
'motion',
|
2016-01-10 13:47:59 +01:00
|
|
|
function($scope, $http, ngDialog, MotionForm, Motion, Category, Mediafile, Tag, User, Workflow, motion) {
|
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');
|
|
|
|
|
|
|
|
$scope.isCollapsed = true;
|
|
|
|
|
2015-11-27 23:09:38 +01:00
|
|
|
// open edit dialog
|
2016-01-10 13:47:59 +01:00
|
|
|
$scope.openDialog = function (motion) {
|
|
|
|
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-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-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});
|
2015-11-23 21:31:15 +01:00
|
|
|
}
|
2016-01-14 22:43:49 +01:00
|
|
|
// reset state
|
2015-11-23 21:31:15 +01:00
|
|
|
$scope.reset_state = function (state_id) {
|
|
|
|
$http.put('/rest/motions/motion/' + motion.id + '/set_state/', {});
|
|
|
|
}
|
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/', {});
|
2015-11-23 21:31:15 +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();
|
2015-11-23 21:31:15 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
])
|
|
|
|
|
|
|
|
.controller('MotionCreateCtrl', [
|
|
|
|
'$scope',
|
|
|
|
'$state',
|
|
|
|
'gettext',
|
|
|
|
'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-10 13:47:59 +01:00
|
|
|
function($scope, $state, gettext, Motion, MotionForm, Category, Config, Mediafile, Tag, User, Workflow) {
|
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 = {};
|
|
|
|
// set default values for create form
|
|
|
|
// ... set preamble config value as text
|
|
|
|
$scope.model.text = Config.get('motions_preamble').value;
|
|
|
|
// ... 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();
|
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) {
|
2015-11-27 23:09:38 +01:00
|
|
|
$scope.closeThisDialog();
|
2015-11-23 21:31:15 +01:00
|
|
|
}
|
|
|
|
);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
])
|
|
|
|
|
|
|
|
.controller('MotionUpdateCtrl', [
|
|
|
|
'$scope',
|
|
|
|
'$state',
|
|
|
|
'Motion',
|
|
|
|
'Category',
|
|
|
|
'Config',
|
|
|
|
'Mediafile',
|
2016-01-10 13:47:59 +01:00
|
|
|
'MotionForm',
|
2015-11-23 21:31:15 +01:00
|
|
|
'Tag',
|
|
|
|
'User',
|
|
|
|
'Workflow',
|
|
|
|
'motion',
|
2016-01-10 13:47:59 +01:00
|
|
|
function($scope, $state, Motion, Category, Config, Mediafile, MotionForm, Tag, User, Workflow, 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;
|
|
|
|
}
|
|
|
|
}
|
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-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) {
|
|
|
|
$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',
|
|
|
|
'$state',
|
|
|
|
'gettextCatalog',
|
|
|
|
'MotionPoll',
|
|
|
|
'MotionPollForm',
|
|
|
|
'motionpoll',
|
|
|
|
'voteNumber',
|
|
|
|
function($scope, $state, gettextCatalog, MotionPoll, MotionPollForm, motionpoll, voteNumber) {
|
|
|
|
// set initial values for form model
|
|
|
|
$scope.model = motionpoll;
|
|
|
|
$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',
|
|
|
|
'gettext',
|
|
|
|
'Category',
|
|
|
|
'Motion',
|
|
|
|
'User',
|
|
|
|
function($scope, gettext, Category, Motion, User) {
|
|
|
|
// set initial data for csv import
|
|
|
|
$scope.motions = []
|
|
|
|
$scope.separator = ',';
|
|
|
|
$scope.encoding = 'UTF-8';
|
|
|
|
$scope.encodingOptions = ['UTF-8', 'ISO-8859-1'];
|
|
|
|
$scope.csv = {
|
|
|
|
content: null,
|
|
|
|
header: true,
|
|
|
|
headerVisible: false,
|
|
|
|
separator: $scope.separator,
|
|
|
|
separatorVisible: false,
|
|
|
|
encoding: $scope.encoding,
|
|
|
|
encodingVisible: false,
|
|
|
|
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');
|
|
|
|
if (motion.identifier != '') {
|
|
|
|
// 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');
|
|
|
|
if (motion.submitter != '') {
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (motion.submitter && motion.submitter != '' && !motion.submitters_id) {
|
|
|
|
motion.submitter_create = gettext('New participant will be created.');
|
|
|
|
}
|
|
|
|
// category
|
|
|
|
if (motion.category) {
|
|
|
|
motion.category = motion.category.replace(quotionRe, '$1');
|
|
|
|
if (motion.category != '') {
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (motion.category && motion.category != '' && !motion.category_id) {
|
|
|
|
motion.category_create = gettext('New category will be created.');
|
|
|
|
}
|
|
|
|
$scope.motions.push(motion);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
// import from csv file
|
|
|
|
$scope.import = function () {
|
|
|
|
$scope.csvImporting = true;
|
|
|
|
angular.forEach($scope.motions, function (motion) {
|
|
|
|
if (!motion.importerror) {
|
|
|
|
// create new user if not exist
|
|
|
|
if (!motion.submitters_id) {
|
|
|
|
var index = motion.submitter.indexOf(' ');
|
|
|
|
var user = {
|
|
|
|
first_name: motion.submitter.substr(0, index),
|
|
|
|
last_name: motion.submitter.substr(index+1),
|
|
|
|
groups: []
|
|
|
|
}
|
|
|
|
User.create(user).then(
|
|
|
|
function(success) {
|
|
|
|
// set new user id
|
|
|
|
motion.submitters_id = [success.id];
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
// create new category if not exist
|
|
|
|
if (!motion.category_id) {
|
|
|
|
var category = {
|
|
|
|
name: motion.category,
|
|
|
|
prefix: motion.category.charAt(0)
|
|
|
|
}
|
|
|
|
Category.create(category).then(
|
|
|
|
function(success) {
|
|
|
|
// set new category id
|
|
|
|
motion.category_id = [success.id];
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
Motion.create(motion).then(
|
|
|
|
function(success) {
|
|
|
|
motion.imported = true;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
$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
|
|
|
|
['identifier', 'title', 'text', 'reason', 'submitter', 'category'],
|
|
|
|
// example entries
|
|
|
|
['A1', 'title 1', 'text 1', 'reason 1', 'Submitter A', 'Category A'],
|
|
|
|
['B1', 'title 2', 'text 2', 'reason 2', 'Submitter B', 'Category B'],
|
|
|
|
['' , 'title 3', 'text 3', '', '', '']
|
|
|
|
|
|
|
|
];
|
|
|
|
var csvString = csvRows.join("%0A");
|
|
|
|
element.href = 'data:text/csv;charset=utf-8,' + csvString;
|
|
|
|
element.download = 'motions-example.csv';
|
|
|
|
element.target = '_blank';
|
|
|
|
}
|
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');
|
|
|
|
}
|
|
|
|
);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
]);
|
2015-11-23 21:31:15 +01:00
|
|
|
|
|
|
|
}());
|