Merge pull request #1709 from emanuelschuetze/assignments-rework
Assignments template improvements
This commit is contained in:
commit
6a4cc97469
@ -161,7 +161,6 @@ angular.module('OpenSlidesApp.agenda.site', ['OpenSlidesApp.agenda'])
|
|||||||
}
|
}
|
||||||
$scope.alert = { type: 'danger', msg: message, show: true };
|
$scope.alert = { type: 'danger', msg: message, show: true };
|
||||||
});
|
});
|
||||||
;
|
|
||||||
};
|
};
|
||||||
// delete related item
|
// delete related item
|
||||||
$scope.deleteRelatedItem = function (item) {
|
$scope.deleteRelatedItem = function (item) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<h1 translate>Agenda</h1>
|
<h1 translate>Agenda</h1>
|
||||||
|
|
||||||
<div id="submenu">
|
<div id="submenu">
|
||||||
<a ng-click="newDialog()" ng-dialog-class="ngdialog-theme-plain"os-perms="agenda.can_manage" class="btn btn-primary btn-sm">
|
<a ng-click="newDialog()" os-perms="agenda.can_manage" class="btn btn-primary btn-sm">
|
||||||
<i class="fa fa-plus fa-lg"></i>
|
<i class="fa fa-plus fa-lg"></i>
|
||||||
<translate>New</translate>
|
<translate>New</translate>
|
||||||
</a>
|
</a>
|
||||||
|
24
openslides/assignments/migrations/0002_auto_20151126_2153.py
Normal file
24
openslides/assignments/migrations/0002_auto_20151126_2153.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assignments', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='assignmentoption',
|
||||||
|
name='poll',
|
||||||
|
field=models.ForeignKey(to='assignments.AssignmentPoll', related_name='options'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='assignmentvote',
|
||||||
|
name='option',
|
||||||
|
field=models.ForeignKey(to='assignments.AssignmentOption', related_name='votes'),
|
||||||
|
),
|
||||||
|
]
|
@ -106,7 +106,7 @@ class Assignment(RESTModelMixin, models.Model):
|
|||||||
settings.AUTH_USER_MODEL,
|
settings.AUTH_USER_MODEL,
|
||||||
through='AssignmentRelatedUser')
|
through='AssignmentRelatedUser')
|
||||||
"""
|
"""
|
||||||
Users that a candidates, elected or blocked as candidate.
|
Users that are candidates, elected or blocked as candidate.
|
||||||
|
|
||||||
See AssignmentRelatedUser for more infos.
|
See AssignmentRelatedUser for more infos.
|
||||||
"""
|
"""
|
||||||
@ -180,7 +180,7 @@ class Assignment(RESTModelMixin, models.Model):
|
|||||||
|
|
||||||
def is_blocked(self, user):
|
def is_blocked(self, user):
|
||||||
"""
|
"""
|
||||||
Returns True if the user is blockt for candidature.
|
Returns True if the user is blocked for candidature.
|
||||||
|
|
||||||
Costs one database query.
|
Costs one database query.
|
||||||
"""
|
"""
|
||||||
@ -253,12 +253,11 @@ class Assignment(RESTModelMixin, models.Model):
|
|||||||
yesnoabstain=yesnoabstain)
|
yesnoabstain=yesnoabstain)
|
||||||
poll.set_options({'candidate': user} for user in candidates)
|
poll.set_options({'candidate': user} for user in candidates)
|
||||||
|
|
||||||
# Add all candidates to all agenda items for this assignment
|
# Add all candidates to list of speakers of related agenda item
|
||||||
# TODO: Try to do this in a bulk create
|
# TODO: Try to do this in a bulk create
|
||||||
for item in self.items.all():
|
|
||||||
for candidate in self.candidates:
|
for candidate in self.candidates:
|
||||||
try:
|
try:
|
||||||
Speaker.objects.add(candidate, item)
|
Speaker.objects.add(candidate, self.agenda_item)
|
||||||
except OpenSlidesError:
|
except OpenSlidesError:
|
||||||
# The Speaker is already on the list. Do nothing.
|
# The Speaker is already on the list. Do nothing.
|
||||||
# TODO: Find a smart way not to catch the error concerning AnonymousUser.
|
# TODO: Find a smart way not to catch the error concerning AnonymousUser.
|
||||||
@ -320,7 +319,7 @@ class Assignment(RESTModelMixin, models.Model):
|
|||||||
|
|
||||||
|
|
||||||
class AssignmentVote(RESTModelMixin, BaseVote):
|
class AssignmentVote(RESTModelMixin, BaseVote):
|
||||||
option = models.ForeignKey('AssignmentOption')
|
option = models.ForeignKey('AssignmentOption', related_name='votes')
|
||||||
|
|
||||||
def get_root_rest_element(self):
|
def get_root_rest_element(self):
|
||||||
"""
|
"""
|
||||||
@ -330,7 +329,7 @@ class AssignmentVote(RESTModelMixin, BaseVote):
|
|||||||
|
|
||||||
|
|
||||||
class AssignmentOption(RESTModelMixin, BaseOption):
|
class AssignmentOption(RESTModelMixin, BaseOption):
|
||||||
poll = models.ForeignKey('AssignmentPoll')
|
poll = models.ForeignKey('AssignmentPoll', related_name='options')
|
||||||
candidate = models.ForeignKey(settings.AUTH_USER_MODEL)
|
candidate = models.ForeignKey(settings.AUTH_USER_MODEL)
|
||||||
vote_class = AssignmentVote
|
vote_class = AssignmentVote
|
||||||
|
|
||||||
|
@ -45,8 +45,8 @@ class AssignmentSlide(ProjectorElement):
|
|||||||
view_class=user.get_view_class(),
|
view_class=user.get_view_class(),
|
||||||
view_action='retrieve',
|
view_action='retrieve',
|
||||||
pk=str(user.pk))
|
pk=str(user.pk))
|
||||||
for poll in assignment.polls.all().prefetch_related('assignmentoption_set'):
|
for poll in assignment.polls.all().prefetch_related('options'):
|
||||||
for option in poll.assignmentoption_set.all():
|
for option in poll.options.all():
|
||||||
yield ProjectorRequirement(
|
yield ProjectorRequirement(
|
||||||
view_class=option.candidate.get_view_class(),
|
view_class=option.candidate.get_view_class(),
|
||||||
view_action='retrieve',
|
view_action='retrieve',
|
||||||
|
@ -46,11 +46,11 @@ class AssignmentOptionSerializer(ModelSerializer):
|
|||||||
"""
|
"""
|
||||||
Serializer for assignment.models.AssignmentOption objects.
|
Serializer for assignment.models.AssignmentOption objects.
|
||||||
"""
|
"""
|
||||||
assignmentvote_set = AssignmentVoteSerializer(many=True, read_only=True)
|
votes = AssignmentVoteSerializer(many=True, read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = AssignmentOption
|
model = AssignmentOption
|
||||||
fields = ('candidate', 'assignmentvote_set',)
|
fields = ('candidate', 'votes',)
|
||||||
|
|
||||||
|
|
||||||
class FilterPollListSerializer(ListSerializer):
|
class FilterPollListSerializer(ListSerializer):
|
||||||
@ -75,7 +75,7 @@ class AssignmentAllPollSerializer(ModelSerializer):
|
|||||||
|
|
||||||
Serializes all polls.
|
Serializes all polls.
|
||||||
"""
|
"""
|
||||||
assignmentoption_set = AssignmentOptionSerializer(many=True, read_only=True)
|
options = AssignmentOptionSerializer(many=True, read_only=True)
|
||||||
votes = ListField(
|
votes = ListField(
|
||||||
child=DictField(
|
child=DictField(
|
||||||
child=IntegerField(min_value=-2)),
|
child=IntegerField(min_value=-2)),
|
||||||
@ -88,7 +88,7 @@ class AssignmentAllPollSerializer(ModelSerializer):
|
|||||||
'yesnoabstain',
|
'yesnoabstain',
|
||||||
'description',
|
'description',
|
||||||
'published',
|
'published',
|
||||||
'assignmentoption_set',
|
'options',
|
||||||
'votesvalid',
|
'votesvalid',
|
||||||
'votesinvalid',
|
'votesinvalid',
|
||||||
'votescast',
|
'votescast',
|
||||||
@ -148,7 +148,7 @@ class AssignmentShortPollSerializer(AssignmentAllPollSerializer):
|
|||||||
'yesnoabstain',
|
'yesnoabstain',
|
||||||
'description',
|
'description',
|
||||||
'published',
|
'published',
|
||||||
'assignmentoption_set',
|
'options',
|
||||||
'votesvalid',
|
'votesvalid',
|
||||||
'votesinvalid',
|
'votesinvalid',
|
||||||
'votescast',)
|
'votescast',)
|
||||||
|
@ -9,7 +9,7 @@ angular.module('OpenSlidesApp.assignments', [])
|
|||||||
'Config',
|
'Config',
|
||||||
function (DS, Config) {
|
function (DS, Config) {
|
||||||
return DS.defineResource({
|
return DS.defineResource({
|
||||||
name: 'assignments/poll',
|
name: 'assignments/assignmentpoll',
|
||||||
relations: {
|
relations: {
|
||||||
belongsTo: {
|
belongsTo: {
|
||||||
'assignments/assignment': {
|
'assignments/assignment': {
|
||||||
@ -44,12 +44,13 @@ angular.module('OpenSlidesApp.assignments', [])
|
|||||||
'AssignmentRelatedUser',
|
'AssignmentRelatedUser',
|
||||||
'AssignmentPoll',
|
'AssignmentPoll',
|
||||||
'jsDataModel',
|
'jsDataModel',
|
||||||
function (DS, AssignmentRelatedUser, AssignmentPoll, jsDataModel) {
|
'gettext',
|
||||||
|
function (DS, AssignmentRelatedUser, AssignmentPoll, jsDataModel, gettext) {
|
||||||
var name = 'assignments/assignment';
|
var name = 'assignments/assignment';
|
||||||
return DS.defineResource({
|
return DS.defineResource({
|
||||||
name: name,
|
name: name,
|
||||||
useClass: jsDataModel,
|
useClass: jsDataModel,
|
||||||
agendaSupplement: '(Assignment)',
|
agendaSupplement: '(' + gettext('Election') + ')',
|
||||||
methods: {
|
methods: {
|
||||||
getResourceName: function () {
|
getResourceName: function () {
|
||||||
return name;
|
return name;
|
||||||
@ -73,6 +74,10 @@ angular.module('OpenSlidesApp.assignments', [])
|
|||||||
'assignments/relateduser': {
|
'assignments/relateduser': {
|
||||||
localField: 'assignment_related_users',
|
localField: 'assignment_related_users',
|
||||||
foreignKey: 'assignment_id',
|
foreignKey: 'assignment_id',
|
||||||
|
},
|
||||||
|
'assignments/assignmentpoll': {
|
||||||
|
localField: 'polls',
|
||||||
|
foreignKey: 'assignment_id',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,9 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
|
|||||||
resolve: {
|
resolve: {
|
||||||
assignment: function(Assignment, $stateParams) {
|
assignment: function(Assignment, $stateParams) {
|
||||||
return Assignment.find($stateParams.id);
|
return Assignment.find($stateParams.id);
|
||||||
|
},
|
||||||
|
users: function(User) {
|
||||||
|
return User.findAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -54,10 +57,93 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
.controller('AssignmentListCtrl', function($scope, Assignment, phases) {
|
// Provide generic assignment form fields for create and update view
|
||||||
|
.factory('AssignmentFormFieldFactory', [
|
||||||
|
'gettext',
|
||||||
|
function (gettext) {
|
||||||
|
return {
|
||||||
|
getFormFields: function () {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
key: 'title',
|
||||||
|
type: 'input',
|
||||||
|
templateOptions: {
|
||||||
|
label: gettext('Title'),
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'description',
|
||||||
|
type: 'textarea',
|
||||||
|
templateOptions: {
|
||||||
|
label: gettext('Description')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'open_posts',
|
||||||
|
type: 'input',
|
||||||
|
templateOptions: {
|
||||||
|
label: gettext('Number of members to be elected'),
|
||||||
|
type: 'number',
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'poll_description_default',
|
||||||
|
type: 'input',
|
||||||
|
templateOptions: {
|
||||||
|
label: gettext('Default comment on the ballot paper')
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
// Provide generic assignmentpoll form fields for create and update view
|
||||||
|
.factory('AssignmentPollFormFieldFactory', [
|
||||||
|
'gettext',
|
||||||
|
function (gettext) {
|
||||||
|
return {
|
||||||
|
getFormFields: function () {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
key: 'description',
|
||||||
|
type: 'input',
|
||||||
|
templateOptions: {
|
||||||
|
label: gettext('Comment on the ballot paper')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'yes',
|
||||||
|
type: 'input',
|
||||||
|
templateOptions: {
|
||||||
|
label: gettext('Yes'),
|
||||||
|
type: 'number',
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'poll_description_default',
|
||||||
|
type: 'input',
|
||||||
|
templateOptions: {
|
||||||
|
label: gettext('Default comment on the ballot paper')
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
.controller('AssignmentListCtrl', [
|
||||||
|
'$scope',
|
||||||
|
'ngDialog',
|
||||||
|
'Assignment',
|
||||||
|
'phases',
|
||||||
|
function($scope, ngDialog, Assignment, phases) {
|
||||||
Assignment.bindAll({}, $scope, 'assignments');
|
Assignment.bindAll({}, $scope, 'assignments');
|
||||||
// get all item types via OPTIONS request
|
// get all item types via OPTIONS request
|
||||||
$scope.phases = phases.data.actions.POST.phase.choices;
|
$scope.phases = phases.data.actions.POST.phase.choices;
|
||||||
|
$scope.alert = {};
|
||||||
|
|
||||||
// setup table sorting
|
// setup table sorting
|
||||||
$scope.sortColumn = 'title';
|
$scope.sortColumn = 'title';
|
||||||
@ -71,37 +157,323 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
|
|||||||
$scope.sortColumn = column;
|
$scope.sortColumn = column;
|
||||||
};
|
};
|
||||||
|
|
||||||
// delete selected assignment
|
// open new customslide dialog
|
||||||
$scope.delete = function (assignment) {
|
$scope.newDialog = function () {
|
||||||
Assignment.destroy(assignment.id);
|
ngDialog.open({
|
||||||
|
template: 'static/templates/assignments/assignment-form.html',
|
||||||
|
controller: 'AssignmentCreateCtrl',
|
||||||
|
className: 'ngdialog-theme-default wide-form'
|
||||||
|
});
|
||||||
};
|
};
|
||||||
})
|
// edit view of related item (content object)
|
||||||
|
$scope.editDialog = function (assignment) {
|
||||||
.controller('AssignmentDetailCtrl', function($scope, Assignment, assignment) {
|
ngDialog.open({
|
||||||
Assignment.bindOne(assignment.id, $scope, 'assignment');
|
template: 'static/templates/assignments/assignment-form.html',
|
||||||
Assignment.loadRelations(assignment, 'agenda_item');
|
controller: 'AssignmentUpdateCtrl',
|
||||||
})
|
className: 'ngdialog-theme-default wide-form',
|
||||||
|
resolve: {
|
||||||
.controller('AssignmentCreateCtrl', function($scope, $state, Assignment) {
|
assignment: function(Assignment) {
|
||||||
$scope.assignment = {};
|
return Assignment.find(assignment.id);
|
||||||
$scope.save = function(assignment) {
|
|
||||||
Assignment.create(assignment).then(
|
|
||||||
function(success) {
|
|
||||||
$state.go('assignments.assignment.list');
|
|
||||||
}
|
}
|
||||||
);
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
})
|
// save changed item
|
||||||
|
|
||||||
.controller('AssignmentUpdateCtrl', function($scope, $state, Assignment, assignment) {
|
|
||||||
$scope.assignment = assignment; // do not use .binOne(...) so autoupdate is not activated
|
|
||||||
$scope.save = function (assignment) {
|
$scope.save = function (assignment) {
|
||||||
Assignment.save(assignment).then(
|
Assignment.save(assignment).then(
|
||||||
function(success) {
|
function(success) {
|
||||||
$state.go('assignments.assignment.list');
|
assignment.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 all selected assignments
|
||||||
|
$scope.deleteMultiple = function () {
|
||||||
|
angular.forEach($scope.assignments, function (assignment) {
|
||||||
|
if (assignment.selected)
|
||||||
|
Assignment.destroy(assignment.id);
|
||||||
|
});
|
||||||
|
$scope.isDeleteMode = false;
|
||||||
|
$scope.uncheckAll();
|
||||||
|
};
|
||||||
|
// delete single assignment
|
||||||
|
$scope.delete = function (assignment) {
|
||||||
|
Assignment.destroy(assignment.id);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
.controller('AssignmentDetailCtrl', [
|
||||||
|
'$scope',
|
||||||
|
'$http',
|
||||||
|
'ngDialog',
|
||||||
|
'operator',
|
||||||
|
'Assignment',
|
||||||
|
'User',
|
||||||
|
'assignment',
|
||||||
|
function($scope, $http, ngDialog, operator, Assignment, User, assignment) {
|
||||||
|
User.bindAll({}, $scope, 'users');
|
||||||
|
Assignment.bindOne(assignment.id, $scope, 'assignment');
|
||||||
|
Assignment.loadRelations(assignment, 'agenda_item');
|
||||||
|
$scope.candidate = {};
|
||||||
|
$scope.alert = {};
|
||||||
|
// add (nominate) candidate
|
||||||
|
$scope.addCandidate = function (userId) {
|
||||||
|
$http.post('/rest/assignments/assignment/' + assignment.id + '/candidature_other/', {'user': userId})
|
||||||
|
.success(function(data){
|
||||||
|
$scope.alert.show = false;
|
||||||
|
})
|
||||||
|
.error(function(data){
|
||||||
|
$scope.alert = { type: 'danger', msg: data.detail, show: true };
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// remove candidate
|
||||||
|
$scope.removeCandidate = function (userId) {
|
||||||
|
$http.delete('/rest/assignments/assignment/' + assignment.id + '/candidature_other/',
|
||||||
|
{headers: {'Content-Type': 'application/json'},
|
||||||
|
data: JSON.stringify({user: userId})})
|
||||||
|
.error(function(data){
|
||||||
|
$scope.alert = { type: 'danger', msg: data.detail, show: true };
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// remove blocked candidate from "block-list"
|
||||||
|
$scope.removeBlockedCandidate = function (userId) {
|
||||||
|
$http.delete('/rest/assignments/assignment/' + assignment.id + '/candidature_other/',
|
||||||
|
{headers: {'Content-Type': 'application/json'},
|
||||||
|
data: JSON.stringify({user: userId})})
|
||||||
|
.error(function(data){
|
||||||
|
$scope.alert = { type: 'danger', msg: data.detail, show: true };
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// add me (nominate self as candidate)
|
||||||
|
$scope.addMe = function () {
|
||||||
|
$http.post('/rest/assignments/assignment/' + assignment.id + '/candidature_self/', {})
|
||||||
|
.success(function(data){
|
||||||
|
$scope.alert.show = false;
|
||||||
|
})
|
||||||
|
.error(function(data){
|
||||||
|
$scope.alert = { type: 'danger', msg: data.detail, show: true };
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// remove me (withdraw own candidature)
|
||||||
|
$scope.removeMe = function () {
|
||||||
|
$http.delete('/rest/assignments/assignment/' + assignment.id + '/candidature_self/')
|
||||||
|
.success(function(data){
|
||||||
|
$scope.alert.show = false;
|
||||||
|
})
|
||||||
|
.error(function(data){
|
||||||
|
$scope.alert = { type: 'danger', msg: data.detail, show: true };
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// check if current user is already a candidate (status=1)
|
||||||
|
$scope.isCandidate = function () {
|
||||||
|
var check = assignment.assignment_related_users.map( function(candidate) {
|
||||||
|
if ( candidate.status == 1)
|
||||||
|
return candidate.user_id;
|
||||||
|
}).indexOf(operator.user.id);
|
||||||
|
if (check > -1)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
// create new ballot
|
||||||
|
$scope.createBallot = function () {
|
||||||
|
$http.post('/rest/assignments/assignment/' + assignment.id + '/create_poll/')
|
||||||
|
.success(function(data){
|
||||||
|
$scope.alert.show = false;
|
||||||
|
})
|
||||||
|
.error(function(data){
|
||||||
|
$scope.alert = { type: 'danger', msg: data.detail, show: true };
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// delete ballt
|
||||||
|
$scope.deleteBallot = function (poll) {
|
||||||
|
poll.DSDestroy();
|
||||||
|
}
|
||||||
|
// edit poll dialog
|
||||||
|
$scope.editPollDialog = function (poll) {
|
||||||
|
ngDialog.open({
|
||||||
|
template: 'static/templates/assignments/assignmentpoll-form.html',
|
||||||
|
controller: 'AssignmentPollUpdateCtrl',
|
||||||
|
className: 'ngdialog-theme-default',
|
||||||
|
resolve: {
|
||||||
|
assignmentpoll: function(AssignmentPoll) {
|
||||||
|
return AssignmentPoll.find(poll.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// publish ballot
|
||||||
|
$scope.publishBallot = function () {
|
||||||
|
// TODO poll.DSUpdate()
|
||||||
|
$http.put('/rest/assignments/assignment/' + assignment.id + '/publish_poll/')
|
||||||
|
.success(function(data){
|
||||||
|
$scope.alert.show = false;
|
||||||
|
})
|
||||||
|
.error(function(data){
|
||||||
|
$scope.alert = { type: 'danger', msg: data.detail, show: true };
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
.controller('AssignmentCreateCtrl', [
|
||||||
|
'$scope',
|
||||||
|
'$state',
|
||||||
|
'Assignment',
|
||||||
|
'AssignmentFormFieldFactory',
|
||||||
|
function($scope, $state, Assignment, AssignmentFormFieldFactory) {
|
||||||
|
$scope.assignment = {};
|
||||||
|
// get all form fields
|
||||||
|
$scope.formFields = AssignmentFormFieldFactory.getFormFields();
|
||||||
|
|
||||||
|
// save assignment
|
||||||
|
$scope.save = function(assignment) {
|
||||||
|
Assignment.create(assignment).then(
|
||||||
|
function(success) {
|
||||||
|
$scope.closeThisDialog();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
});
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
.controller('AssignmentUpdateCtrl', [
|
||||||
|
'$scope',
|
||||||
|
'$state',
|
||||||
|
'Assignment',
|
||||||
|
'AssignmentFormFieldFactory',
|
||||||
|
'assignment',
|
||||||
|
function($scope, $state, Assignment, AssignmentFormFieldFactory, assignment) {
|
||||||
|
// set initial values for form model
|
||||||
|
$scope.model = assignment;
|
||||||
|
// get all form fields
|
||||||
|
$scope.formFields = AssignmentFormFieldFactory.getFormFields();
|
||||||
|
|
||||||
|
// save assignment
|
||||||
|
$scope.save = function (assignment) {
|
||||||
|
Assignment.save(assignment).then(
|
||||||
|
function(success) {
|
||||||
|
$scope.closeThisDialog();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
.controller('AssignmentPollUpdateCtrl', [
|
||||||
|
'$scope',
|
||||||
|
'$state',
|
||||||
|
'gettext',
|
||||||
|
'AssignmentPoll',
|
||||||
|
'assignmentpoll',
|
||||||
|
function($scope, $state, gettext, AssignmentPoll, assignmentpoll) {
|
||||||
|
// set initial values for form model
|
||||||
|
$scope.model = assignmentpoll;
|
||||||
|
$scope.formFields = [];
|
||||||
|
// add dynamic form fields
|
||||||
|
assignmentpoll.options.forEach(function(option) {
|
||||||
|
$scope.formFields.push(
|
||||||
|
{
|
||||||
|
noFormControl: true,
|
||||||
|
template: '<strong>User#' + option.candidate_id + '</strong>'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'yes_' + option.candidate_id,
|
||||||
|
type: 'input',
|
||||||
|
templateOptions: {
|
||||||
|
label: gettext('Yes'),
|
||||||
|
type: 'number',
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'no_' + option.candidate_id,
|
||||||
|
type: 'input',
|
||||||
|
templateOptions: {
|
||||||
|
label: gettext('No'),
|
||||||
|
type: 'number',
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key:'abstain_' + option.candidate_id,
|
||||||
|
type: 'input',
|
||||||
|
templateOptions: {
|
||||||
|
label: gettext('Abstain'),
|
||||||
|
type: 'number',
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
// add general form fields
|
||||||
|
$scope.formFields.push(
|
||||||
|
{
|
||||||
|
key: 'votesvalid',
|
||||||
|
type: 'input',
|
||||||
|
templateOptions: {
|
||||||
|
label: gettext('Votes valid'),
|
||||||
|
type: 'number'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'votesinvalid',
|
||||||
|
type: 'input',
|
||||||
|
templateOptions: {
|
||||||
|
label: gettext('Votes invalid'),
|
||||||
|
type: 'number'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'votescast',
|
||||||
|
type: 'input',
|
||||||
|
templateOptions: {
|
||||||
|
label: gettext('Votes cast'),
|
||||||
|
type: 'number'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// TODO: update description in separat request
|
||||||
|
// (without vote result values)
|
||||||
|
{
|
||||||
|
key: 'description',
|
||||||
|
type: 'input',
|
||||||
|
templateOptions: {
|
||||||
|
label: gettext('Comment on the ballot paper')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// save assignment
|
||||||
|
$scope.save = function (poll) {
|
||||||
|
var votes = [];
|
||||||
|
assignmentpoll.options.forEach(function(option) {
|
||||||
|
votes.push({
|
||||||
|
"Yes": poll['yes_' + option.candidate_id],
|
||||||
|
"No": poll['no_' + option.candidate_id],
|
||||||
|
"Abstain": poll['abstain_' + option.candidate_id]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
poll.DSUpdate({
|
||||||
|
assignment_id: poll.assignment_id,
|
||||||
|
votes: votes,
|
||||||
|
votesvalid: poll.votesvalid,
|
||||||
|
votesinvalid: poll.votesinvalid,
|
||||||
|
votescast: poll.votescast
|
||||||
|
})
|
||||||
|
.then(function(success) {
|
||||||
|
$scope.closeThisDialog();
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
}());
|
}());
|
||||||
|
@ -24,18 +24,98 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Agenda Item: {{ assignment.agenda_item }}
|
|
||||||
|
|
||||||
|
|
||||||
<h3 translate>Description</h3>
|
<h3 translate>Description</h3>
|
||||||
<div class="white-space-pre-line">{{ assignment.description }}</div>
|
<div class="white-space-pre-line">{{ assignment.description }}</div>
|
||||||
|
|
||||||
<h3 translate>Candidates</h3>
|
<h3 translate>Candidates</h3>
|
||||||
<ul>
|
<ol>
|
||||||
<li ng-repeat="related_user in assignment.assignment_related_users" ng-if="related_user.status == 1">
|
<li ng-repeat="related_user in assignment.assignment_related_users" ng-if="related_user.status == 1">
|
||||||
User: {{ related_user.user.get_full_name() }}
|
<a ui-sref="users.user.detail({id: related_user.user_id})">{{ related_user.user.get_full_name() }}</a>
|
||||||
</li>
|
<button os-perms="assignments.can_manage" ng-click="removeCandidate(related_user.user_id)"
|
||||||
</ul>
|
class="btn btn-default btn-xs">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</button>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<alert ng-show="alert.show" type="{{ alert.type }}" ng-click="alert={}" close="alert={}">
|
||||||
|
{{alert.msg}}
|
||||||
|
</alert>
|
||||||
|
<div os-perms="assignments.can_nominate_other" class="input-group">
|
||||||
|
<ui-select ng-model="candidate.selected" ng-change="addCandidate(candidate.selected.id)">
|
||||||
|
<ui-select-match placeholder="{{ 'Select or search a participant...' | translate }}">
|
||||||
|
{{ $select.selected.get_full_name() }}
|
||||||
|
</ui-select-match>
|
||||||
|
<ui-select-choices repeat="user in users | filter: $select.search">
|
||||||
|
<div ng-bind-html="user.get_full_name() | highlight: $select.search"></div>
|
||||||
|
</ui-select-choices>
|
||||||
|
</ui-select>
|
||||||
|
<span class="input-group-btn">
|
||||||
|
<a ng-click="candidate={}" class="btn btn-default">
|
||||||
|
<i class="fa fa-times-circle"></i>
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p os-perm="assignments.can_nominate_self">
|
||||||
|
<button ng-if="!isCandidate()" ng-click="addMe()" class="btn btn-default">
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
<translate>Add me</translate>
|
||||||
|
</button>
|
||||||
|
<button ng-if="isCandidate()" ng-click="removeMe()" class="btn btn-default">
|
||||||
|
<i class="fa fa-minus"></i>
|
||||||
|
<translate>Remove me</translate>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h3 translate>Election result</h3>
|
<h3 translate>Election result</h3>
|
||||||
<!-- TODO -->
|
<button os-perms="assignments.can_manage" ng-click="createBallot()" class="btn btn-default btn-sm">
|
||||||
|
<i class="fa fa-bar-chart fa-lg"></i>
|
||||||
|
<translate>New ballot</translate>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<uib-tabset class="spacer">
|
||||||
|
<uib-tab ng-repeat="poll in assignment.polls" heading="Ballot {{$index+1}}">
|
||||||
|
<div os-perms="assignments.can_manage" class="spacer">
|
||||||
|
<button ng-click="editPollDialog(poll)"
|
||||||
|
class="btn btn-default btn-sm">
|
||||||
|
<i class="fa fa-pencil"></i>
|
||||||
|
<translate>Edit</translate>
|
||||||
|
</button>
|
||||||
|
<!-- angular requires to open the link in new tab with "target='_blank'".
|
||||||
|
Otherwise the pdf url can't be open in same window; angular redirects to "/". -->
|
||||||
|
<a ui-sref="assignmentpoll_pdf({poll_pk: poll.id})" target="_blank"
|
||||||
|
class="btn btn-default btn-sm">
|
||||||
|
<i class="fa fa-file-pdf-o"></i> Ballot paper
|
||||||
|
</a>
|
||||||
|
<button os-perms-lite="assignments.can_manage" ng-if="!poll.published" ng-click="publishBallot(poll)"
|
||||||
|
class="btn btn-primary btn-sm">
|
||||||
|
<i class="fa fa-globe"></i>
|
||||||
|
<translate>Publish result</translate>
|
||||||
|
</button>
|
||||||
|
<button os-perms-lite="assignments.can_manage" ng-if="poll.published" ng-click="unpublishBallot(poll)"
|
||||||
|
class="btn btn-default btn-sm">
|
||||||
|
<i class="fa fa-globe"></i>
|
||||||
|
<translate>Unpublish result</translate>
|
||||||
|
</button>
|
||||||
|
<a ng-click="deleteBallot(poll)" class="btn btn-default btn-sm">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
<translate>Delete</translate>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="results">
|
||||||
|
<div ng-repeat="option in poll.options">
|
||||||
|
<strong>User#{{ option.candidate_id }}</strong>
|
||||||
|
<div ng-if="option.votes.length > 0">
|
||||||
|
<div ng-repeat="vote in option.votes">
|
||||||
|
{{ vote.value}}: {{ vote.weight}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
Valid votes: {{ poll.votesvalid }}<br>
|
||||||
|
Invalid votes: {{ poll.votesinvalid }}<br>
|
||||||
|
Votes cast: {{ poll.votescast }}
|
||||||
|
</div>
|
||||||
|
</uib-tab>
|
||||||
|
</uib-tabset>
|
||||||
|
@ -1,31 +1,13 @@
|
|||||||
<h1 ng-if="assignment.id" translate>Edit election</h1>
|
<h1 ng-if="assignment.id" translate>Edit election</h1>
|
||||||
<h1 ng-if="!assignment.id" translate>New election</h1>
|
<h1 ng-if="!assignment.id" translate>New election</h1>
|
||||||
|
|
||||||
<div id="submenu">
|
<form name="assignmentForm" ng-submit="save(model)">
|
||||||
<a ui-sref="assignments.assignment.list" class="btn btn-sm btn-default">
|
<formly-form model="model" fields="formFields">
|
||||||
<i class="fa fa-angle-double-left fa-lg"></i>
|
<button type="submit" ng-disabled="assignmentForm.$invalid" class="btn btn-primary" translate>
|
||||||
<translate>Back to overview</translate>
|
Submit
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form name="assignmentForm">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="inputTitle" translate>Title</label>
|
|
||||||
<input type="text" ng-model="assignment.title" class="form-control" name="inputTitle" required>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="textareaDesciption" translate>Description</label>
|
|
||||||
<textarea ng-model="assignment.description" class="form-control" name="textareaDescription" />
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="inputPosts" translate>Number of members to be elected</label>
|
|
||||||
<input type="number" ng-model="assignment.open_posts" class="form-control" name="inputPosts" required>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit" ng-click="save(assignment)" class="btn btn-primary" translate>
|
|
||||||
Save
|
|
||||||
</button>
|
</button>
|
||||||
<button ui-sref="assignments.assignment.list" class="btn btn-default" translate>
|
<button ng-click="closeThisDialog()" class="btn btn-default" translate>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
|
</formly-form>
|
||||||
</form>
|
</form>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<h1 translate>Elections</h1>
|
<h1 translate>Elections</h1>
|
||||||
|
|
||||||
<div id="submenu">
|
<div id="submenu">
|
||||||
<a ui-sref="assignments.assignment.create" os-perms="assignments.can_manage" class="btn btn-primary btn-sm">
|
<a ng-click="newDialog()" os-perms="assignments.can_manage" class="btn btn-primary btn-sm">
|
||||||
<i class="fa fa-plus fa-lg"></i>
|
<i class="fa fa-plus fa-lg"></i>
|
||||||
<translate>New</translate>
|
<translate>New</translate>
|
||||||
</a>
|
</a>
|
||||||
@ -17,64 +17,129 @@
|
|||||||
|
|
||||||
<div class="row form-group">
|
<div class="row form-group">
|
||||||
<div class="col-sm-8">
|
<div class="col-sm-8">
|
||||||
<!-- TODO: select filter for phases -->
|
<form class="form-inline">
|
||||||
|
<!-- delete mode -->
|
||||||
|
<div os-perms-lite="assignments.can_manage" class="form-group">
|
||||||
|
<label for="deleteSwitcher" translate>Delete mode</label>
|
||||||
|
<switch id="deleteSwitcher" ng-model="isDeleteMode" ng-change="uncheckAll()"
|
||||||
|
on="{{'On'|translate}}" off="{{'Off'|translate}}"
|
||||||
|
class="green wide form-control">
|
||||||
|
</switch>
|
||||||
|
</div>
|
||||||
|
<!-- delete button -->
|
||||||
|
<a ng-show="isDeleteMode && (assignments|filter:{selected:true}).length > 0"
|
||||||
|
os-perms="assignments.can_manage" ng-click="deleteMultiple()"
|
||||||
|
class="btn btn-primary btn-sm form-control">
|
||||||
|
<i class="fa fa-trash fa-lg"></i>
|
||||||
|
<translate>Delete selected elections</translate>
|
||||||
|
</a>
|
||||||
|
<!-- phase filter -->
|
||||||
|
|
||||||
|
<select ng-model="phaseFilter" class="form-control" id="phaseFilter">
|
||||||
|
<option value="" translate>--- Select phase ---</option>
|
||||||
|
<option ng-repeat="phase in phases" value="{{ phase.value }}">{{ phase.display_name }}</option>
|
||||||
|
</select>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-addon"><i class="fa fa-filter"></i></div>
|
||||||
<input type="text" os-focus-me ng-model="filter.search" class="form-control"
|
<input type="text" os-focus-me ng-model="filter.search" class="form-control"
|
||||||
placeholder="{{ 'Filter' | translate}}">
|
placeholder="{{ 'Filter' | translate}}">
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table class="table table-striped table-bordered table-hover">
|
<table class="table table-striped table-bordered table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
<!-- projector column -->
|
||||||
|
<th ng-show="!isDeleteMode" os-perms="core.can_manage_projector" class="firstColumn">
|
||||||
|
<!-- delete selection column -->
|
||||||
|
<th ng-show="isDeleteMode" os-perms-lite="assignments.can_manage" class="firstColumn deleteColumn">
|
||||||
|
<input type="checkbox" ng-model="selectedAll" ng-change="checkAll()">
|
||||||
<th ng-click="toggleSort('title')" class="sortable">
|
<th ng-click="toggleSort('title')" class="sortable">
|
||||||
<translate>Title</translate>
|
<translate>Title</translate>
|
||||||
<i class="pull-right fa" ng-show="sortColumn === 'title' && header.sortable != false"
|
<i class="pull-right fa" ng-show="sortColumn === 'title' && header.sortable != false"
|
||||||
ng-class="reverse ? 'fa-sort-desc' : 'fa-sort-asc'">
|
ng-class="reverse ? 'fa-sort-desc' : 'fa-sort-asc'">
|
||||||
</i>
|
</i>
|
||||||
<th ng-click="toggleSort('open_posts')" class="sortable">
|
<th ng-click="toggleSort('open_posts')" class="sortable optional">
|
||||||
<translate>Posts</translate>
|
<translate>Candidates</translate> / <translate>Posts</translate>
|
||||||
<i class="pull-right fa" ng-show="sortColumn === 'open_posts' && header.sortable != false"
|
<i class="pull-right fa" ng-show="sortColumn === 'open_posts' && header.sortable != false"
|
||||||
ng-class="reverse ? 'fa-sort-desc' : 'fa-sort-asc'">
|
ng-class="reverse ? 'fa-sort-desc' : 'fa-sort-asc'">
|
||||||
</i>
|
</i>
|
||||||
<th ng-click="toggleSort('phase')" class="sortable">
|
<th ng-click="toggleSort('phase')" class="sortable optional">
|
||||||
<translate>State</translate>
|
<translate>Phase</translate>
|
||||||
<i class="pull-right fa" ng-show="sortColumn === 'phase' && header.sortable != false"
|
<i class="pull-right fa" ng-show="sortColumn === 'phase' && header.sortable != false"
|
||||||
ng-class="reverse ? 'fa-sort-desc' : 'fa-sort-asc'">
|
ng-class="reverse ? 'fa-sort-desc' : 'fa-sort-asc'">
|
||||||
</i>
|
</i>
|
||||||
<th os-perms="assignments.can_manage core.can_manage_projector" class="minimum">
|
|
||||||
<translate>Actions</translate>
|
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr ng-repeat="assignment in assignments | filter: filter.search |
|
<tr ng-repeat="assignment in assignments | filter: filter.search | filter: {phase: phaseFilter} |
|
||||||
orderBy: sortColumn:reverse" ng-class="{ 'activeline': assignment.isProjected() }">
|
orderBy: sortColumn:reverse"
|
||||||
<td><a ui-sref="assignments.assignment.detail({id: assignment.id})">{{ assignment.title }}</a>
|
class="animate-item"
|
||||||
<td class="optional">{{ assignment.open_posts }}
|
ng-class="{ 'activeline': assignment.isProjected(), 'selected': assignment.selected }">
|
||||||
<td class="optional">
|
<!-- projector column -->
|
||||||
|
<td ng-show="!isDeleteMode" os-perms-lite="core.can_manage_projector">
|
||||||
|
<a class="btn btn-default btn-sm"
|
||||||
|
ng-class="{ 'btn-primary': assignment.isProjected() }"
|
||||||
|
ng-click="assignment.project()"
|
||||||
|
title="{{ 'Project assignment' | translate }}">
|
||||||
|
<i class="fa fa-video-camera"></i>
|
||||||
|
</a>
|
||||||
|
<!-- delete selection column -->
|
||||||
|
<td ng-show="isDeleteMode" os-perms="assignments.can_manage" class="deleteColumn">
|
||||||
|
<input type="checkbox" ng-model="assignment.selected">
|
||||||
|
<!-- assignment data colums -->
|
||||||
|
<td ng-if="!assignment.quickEdit" ng-mouseover="assignment.hover=true" ng-mouseleave="assignment.hover=false">
|
||||||
|
<strong><a ui-sref="assignments.assignment.detail({id: assignment.id})">{{ assignment.title }}</a></strong>
|
||||||
|
<div os-perms="assignments.can_manage" class="hoverActions" ng-class="{'hiddenDiv': !assignment.hover}">
|
||||||
|
<a href="" ng-click="editDialog(assignment)" translate>Edit</a> |
|
||||||
|
<a href="" ng-click="assignment.quickEdit=true" translate>QuickEdit</a> |
|
||||||
|
<!-- TODO: translate confirm message -->
|
||||||
|
<a href="" class="text-danger"
|
||||||
|
ng-bootbox-confirm="Are you sure you want to delete <b>{{ assignment.title }}</b>?"
|
||||||
|
ng-bootbox-confirm-action="delete(assignment)" translate>Delete</a>
|
||||||
|
</div>
|
||||||
|
<td ng-if="!assignment.quickEdit" class="optional"><span class="badge">{{ assignment.open_posts }}</span>
|
||||||
|
<td ng-if="!assignment.quickEdit" class="optional">
|
||||||
<span class="label" ng-class="{'label-primary': assignment.phase == 0,
|
<span class="label" ng-class="{'label-primary': assignment.phase == 0,
|
||||||
'label-warning': assignment.phase == 1,
|
'label-warning': assignment.phase == 1,
|
||||||
'label-success': assignment.phase == 2 }">
|
'label-success': assignment.phase == 2 }">
|
||||||
{{ phases[assignment.phase].display_name }}
|
{{ phases[assignment.phase].display_name }}
|
||||||
</span>
|
</span>
|
||||||
<td os-perms="assignments.can_manage core.can_manage_projector" class="nobr">
|
<!-- quickEdit columns -->
|
||||||
<!-- project -->
|
<td ng-if="assignment.quickEdit" colspan="3">
|
||||||
<a os-perms="core.can_manage_projector" class="btn btn-default btn-sm"
|
<h4>{{ assignment.title }} <span class="text-muted">– Quick Edit</span></h4>
|
||||||
ng-class="{ 'btn-primary': assignment.isProjected() }"
|
<alert ng-show="alert.show" type="{{ alert.type }}" ng-click="alert={}" close="alert={}">
|
||||||
ng-click="assignment.project()"
|
{{alert.msg}}
|
||||||
title="{{ 'Project election' | translate }}">
|
</alert>
|
||||||
<i class="fa fa-video-camera"></i>
|
<div class="row">
|
||||||
</a>
|
<div class="col-xs-6">
|
||||||
<!-- edit -->
|
<label for="inputTitle" translate>Title</label>
|
||||||
<a ui-sref="assignments.assignment.detail.update({id: assignment.id })" os-perms="assignments.can_manage"
|
<input type="text" ng-model="assignment.title" class="form-control input-sm" id="inputTitle">
|
||||||
class="btn btn-default btn-sm"
|
</div>
|
||||||
title="{{ 'Edit' | translate}}">
|
<div class="col-xs-6">
|
||||||
<i class="fa fa-pencil"></i>
|
<label for="inputPosts" translate>Number of members to be elected</label>
|
||||||
</a>
|
<input type="number" ng-model="assignment.open_posts" class="form-control input-sm" id="inputPosts">
|
||||||
<!-- delete -->
|
</div>
|
||||||
<a os-perms="assignments.can_manage" class="btn btn-danger btn-sm"
|
</div>
|
||||||
ng-bootbox-confirm="Are you sure you want to delete <b>{{ assignment.title }}</b>?"
|
<div class="row">
|
||||||
ng-bootbox-confirm-action="delete(assignment)"
|
<div class="col-xs-6">
|
||||||
title="{{ 'Delete' | translate }}">
|
<label for="selectPhase" translate>Phase</label>
|
||||||
<i class="fa fa-trash-o"></i>
|
<select ng-options="phase.value as phase.display_name for phase in phases"
|
||||||
</a>
|
ng-model="assignment.phase" class="form-control" id="selectPhase">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-6"></div>
|
||||||
|
</div>
|
||||||
|
<div class="spacer">
|
||||||
|
<button ng-click="assignment.quickEdit=false" class="btn btn-default pull-left" translate>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button ng-click="save(assignment)" class="btn btn-primary" translate>
|
||||||
|
Update
|
||||||
|
</button>
|
||||||
|
<a href="" ng-click="editDialog(assignment)"
|
||||||
|
class="pull-right" translate>Edit election...</a>
|
||||||
|
</div>
|
||||||
</table>
|
</table>
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
<h1 translate>Update ballot</h1>
|
||||||
|
|
||||||
|
<form name="assignmentpollForm" ng-submit="save(model)">
|
||||||
|
<formly-form model="model" fields="formFields">
|
||||||
|
<button type="submit" ng-disabled="assignmentForm.$invalid" class="btn btn-primary" translate>
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
<button ng-click="closeThisDialog()" class="btn btn-default" translate>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</formly-form>
|
||||||
|
</form>
|
@ -166,7 +166,7 @@ class AssignmentViewSet(ModelViewSet):
|
|||||||
self.permission_denied(request)
|
self.permission_denied(request)
|
||||||
if not request.user.has_perm('assignments.can_manage'):
|
if not request.user.has_perm('assignments.can_manage'):
|
||||||
if assignment.is_blocked(user):
|
if assignment.is_blocked(user):
|
||||||
raise ValidationError({'detail': _('User %s does not want to be an candidate.') % user})
|
raise ValidationError({'detail': _('User %s does not want to be a candidate. Only a manager can do this.') % user})
|
||||||
if assignment.is_elected(user):
|
if assignment.is_elected(user):
|
||||||
raise ValidationError({'detail': _('User %s is already elected.') % user})
|
raise ValidationError({'detail': _('User %s is already elected.') % user})
|
||||||
# If the user is already a candidate he can be nominated nevertheless.
|
# If the user is already a candidate he can be nominated nevertheless.
|
||||||
@ -344,7 +344,7 @@ class AssignmentPDF(PDFView):
|
|||||||
length = len(vote_results)
|
length = len(vote_results)
|
||||||
for candidate, poll_list in vote_results.iteritems():
|
for candidate, poll_list in vote_results.iteritems():
|
||||||
row = []
|
row = []
|
||||||
candidate_string = candidate.clean_name
|
candidate_string = candidate.get_short_name()
|
||||||
if candidate in elected_candidates:
|
if candidate in elected_candidates:
|
||||||
candidate_string = "* " + candidate_string
|
candidate_string = "* " + candidate_string
|
||||||
if candidate.name_suffix and length < 20:
|
if candidate.name_suffix and length < 20:
|
||||||
@ -549,7 +549,7 @@ class AssignmentPollPDF(PDFView):
|
|||||||
candidate = option.candidate
|
candidate = option.candidate
|
||||||
cell.append(Paragraph("<font name='circlefont' size='15'>%s</font> \
|
cell.append(Paragraph("<font name='circlefont' size='15'>%s</font> \
|
||||||
<font name='Ubuntu'>%s</font>" %
|
<font name='Ubuntu'>%s</font>" %
|
||||||
(circle, candidate.clean_name), stylesheet['Ballot_option_name']))
|
(circle, candidate.get_short_name()), stylesheet['Ballot_option_name']))
|
||||||
if candidate.structure_level:
|
if candidate.structure_level:
|
||||||
cell.append(Paragraph(
|
cell.append(Paragraph(
|
||||||
"(%s)" % candidate.structure_level,
|
"(%s)" % candidate.structure_level,
|
||||||
|
@ -381,7 +381,7 @@ angular.module('OpenSlidesApp.core.site', [
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
// Provide generic motion form fields for create and update view
|
// Provide generic customslide form fields for create and update view
|
||||||
.factory('CustomslideFormFieldFactory', [
|
.factory('CustomslideFormFieldFactory', [
|
||||||
'gettext',
|
'gettext',
|
||||||
'CKEditorOptions',
|
'CKEditorOptions',
|
||||||
|
Loading…
Reference in New Issue
Block a user