Merge pull request #3740 from FinnStutzenstein/voting-plugin

Changes for the voting plugin and usability improvements
This commit is contained in:
Emanuel Schütze 2018-05-29 15:02:37 +02:00 committed by GitHub
commit 407b640f80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 50 additions and 19 deletions

View File

@ -40,11 +40,16 @@ class AssignmentAccessPermissions(BaseAccessPermissions):
if has_perm(user, 'assignments.can_see') and has_perm(user, 'assignments.can_manage'): if has_perm(user, 'assignments.can_see') and has_perm(user, 'assignments.can_manage'):
data = full_data data = full_data
elif has_perm(user, 'assignments.can_see'): elif has_perm(user, 'assignments.can_see'):
# Exclude unpublished polls. # Exclude unpublished poll votes.
data = [] data = []
for full in full_data: for full in full_data:
full_copy = full.copy() full_copy = full.copy()
full_copy['polls'] = [poll for poll in full['polls'] if poll['published']] polls = full_copy['polls']
for poll in polls:
if not poll['published']:
for option in poll['options']:
option['votes'] = [] # clear votes for not published polls
poll['has_votes'] = False # A user should see, if there are votes.
data.append(full_copy) data.append(full_copy)
else: else:
data = [] data = []

View File

@ -337,9 +337,12 @@ angular.module('OpenSlidesApp.assignments', [])
getResourceName: function () { getResourceName: function () {
return name; return name;
}, },
getAgendaTitle: function () { getTitle: function () {
return this.title; return this.title;
}, },
getAgendaTitle: function () {
return this.getTitle();
},
// link name which is shown in search result // link name which is shown in search result
getSearchResultName: function () { getSearchResultName: function () {
return this.getAgendaTitle(); return this.getAgendaTitle();

View File

@ -716,14 +716,12 @@ angular.module('OpenSlidesApp.assignments.site', [
defaultValue[vote.value.toLowerCase()] = vote.weight; defaultValue[vote.value.toLowerCase()] = vote.weight;
}); });
$scope.formFields.push( var columnClass = (assignmentpoll.pollmethod === 'yna') ? 'col-xs-4' : 'col-xs-6';
{ var columns = [
noFormControl: true,
template: '<strong>' + option.candidate.get_full_name() + '</strong>'
},
{ {
key: 'yes_' + option.candidate_id, key: 'yes_' + option.candidate_id,
type: 'input', type: 'input',
className: columnClass + ' no-padding-left',
templateOptions: { templateOptions: {
label: gettextCatalog.getString('Yes'), label: gettextCatalog.getString('Yes'),
type: 'number', type: 'number',
@ -735,6 +733,8 @@ angular.module('OpenSlidesApp.assignments.site', [
{ {
key: 'no_' + option.candidate_id, key: 'no_' + option.candidate_id,
type: 'input', type: 'input',
className: columnClass + ' no-padding-left' +
(assignmentpoll.pollmethod === 'yn' ? ' no-padding-right' : ''),
templateOptions: { templateOptions: {
label: gettextCatalog.getString('No'), label: gettextCatalog.getString('No'),
type: 'number', type: 'number',
@ -743,12 +743,12 @@ angular.module('OpenSlidesApp.assignments.site', [
}, },
defaultValue: defaultValue.no defaultValue: defaultValue.no
} }
); ];
if (assignmentpoll.pollmethod == 'yna'){ if (assignmentpoll.pollmethod == 'yna'){
$scope.formFields.push( columns.push({
{
key:'abstain_' + option.candidate_id, key:'abstain_' + option.candidate_id,
type: 'input', type: 'input',
className: columnClass + ' no-padding-left no-padding-right',
templateOptions: { templateOptions: {
label: gettextCatalog.getString('Abstain'), label: gettextCatalog.getString('Abstain'),
type: 'number', type: 'number',
@ -758,6 +758,14 @@ angular.module('OpenSlidesApp.assignments.site', [
defaultValue: defaultValue.abstain defaultValue: defaultValue.abstain
}); });
} }
$scope.formFields.push({
noFormControl: true,
template: '<strong>' + option.candidate.get_full_name() + '</strong>'
},
{
className: 'row',
fieldGroup: columns,
});
} else { // votes method } else { // votes method
if (option.votes.length) { if (option.votes.length) {
defaultValue = option.votes[0].weight; defaultValue = option.votes[0].weight;

View File

@ -191,8 +191,10 @@ class AssignmentViewSet(ModelViewSet):
if not assignment.candidates.exists(): if not assignment.candidates.exists():
raise ValidationError({'detail': _('Can not create ballot because there are no candidates.')}) raise ValidationError({'detail': _('Can not create ballot because there are no candidates.')})
with transaction.atomic(): with transaction.atomic():
assignment.create_poll() poll = assignment.create_poll()
return Response({'detail': _('Ballot created successfully.')}) return Response({
'detail': _('Ballot created successfully.'),
'createdPollId': poll.pk})
@detail_route(methods=['post']) @detail_route(methods=['post'])
def sort_related_users(self, request, pk=None): def sort_related_users(self, request, pk=None):

View File

@ -155,6 +155,7 @@ angular.module('OpenSlidesApp.core', [
'User', 'User',
'Group', 'Group',
function (User, Group) { function (User, Group) {
var setUserCallbacks = [];
var operator = { var operator = {
user: null, user: null,
perms: [], perms: [],
@ -167,6 +168,10 @@ angular.module('OpenSlidesApp.core', [
} else { } else {
operator.user = null; operator.user = null;
} }
operator.reloadPerms();
_.forEach(setUserCallbacks, function (cb) {
cb(operator.user);
});
}, },
// Returns true if the operator has at least one perm of the perms-list. // Returns true if the operator has at least one perm of the perms-list.
hasPerms: function(perms) { hasPerms: function(perms) {
@ -191,6 +196,9 @@ angular.module('OpenSlidesApp.core', [
} }
return _.indexOf(groups, group.id) > -1; return _.indexOf(groups, group.id) > -1;
}, },
registerSetUserCallback: function (cb) {
setUserCallbacks.push(cb);
},
}; };
return operator; return operator;
} }
@ -715,6 +723,7 @@ angular.module('OpenSlidesApp.core', [
}); });
} }
scope.content = '';
if (attributes.content) { if (attributes.content) {
attributes.$observe('content', function (content) { attributes.$observe('content', function (content) {
scope.content = content; scope.content = content;
@ -816,8 +825,9 @@ angular.module('OpenSlidesApp.core', [
* provide also html markup for the messages. There are 4 types: 'info', * provide also html markup for the messages. There are 4 types: 'info',
* 'success', 'warning', 'error'. The timeout is for autodeleting the message. * 'success', 'warning', 'error'. The timeout is for autodeleting the message.
* Args that could be provided: * Args that could be provided:
* - timeout: Milliseconds until autoclose the message * - timeout: Milliseconds until autoclose the message (default: not set, no auto close)
* - noClose: Whether to show the close button*/ * - noClose: Whether to show the close button (default: false)
*/
.factory('Messaging', [ .factory('Messaging', [
'$timeout', '$timeout',
function($timeout) { function($timeout) {

View File

@ -6,7 +6,7 @@
ng-click="model.project(defaultProjectorId, arg)" ng-click="model.project(defaultProjectorId, arg)"
ng-class="{ 'btn-primary': inArray(model.isProjected(arg), defaultProjectorId) }"> ng-class="{ 'btn-primary': inArray(model.isProjected(arg), defaultProjectorId) }">
<i class="fa fa-video-camera"></i> <i class="fa fa-video-camera"></i>
{{ content }} {{ content | translate }}
</button> </button>
<button type="button" class="btn btn-default btn-sm slimDropDown" <button type="button" class="btn btn-default btn-sm slimDropDown"
ng-class="{ 'btn-primary': (model.isProjected(arg).length && !inArray(model.isProjected(arg), defaultProjectorId)) }" ng-class="{ 'btn-primary': (model.isProjected(arg).length && !inArray(model.isProjected(arg), defaultProjectorId)) }"

View File

@ -346,6 +346,7 @@
</div> </div>
<div class="col-sm-4"> <div class="col-sm-4">
<h3 ng-if="motion.polls.length > 0" translate>Voting result</h3> <h3 ng-if="motion.polls.length > 0" translate>Voting result</h3>
<template-hook hook-name="motionPollVotingHeader"></template-hook>
<ol> <ol>
<li ng-controller="MotionPollDetailCtrl" ng-repeat="poll in motion.polls" class="spacer" <li ng-controller="MotionPollDetailCtrl" ng-repeat="poll in motion.polls" class="spacer"
ng-if="poll.has_votes || operator.hasPerms('motions.can_manage')"> ng-if="poll.has_votes || operator.hasPerms('motions.can_manage')">

View File

@ -115,7 +115,7 @@ angular.module('OpenSlidesApp.users.pdf', ['OpenSlidesApp.core.pdf'])
var createInstance = function(userList) { var createInstance = function(userList) {
var creadeUserHeadLine = function(user) { var createUserHeadLine = function(user) {
var titleLine = []; var titleLine = [];
titleLine.push({ titleLine.push({
text: user.get_short_name(), text: user.get_short_name(),
@ -260,7 +260,7 @@ angular.module('OpenSlidesApp.users.pdf', ['OpenSlidesApp.core.pdf'])
var getContent = function() { var getContent = function() {
var content = []; var content = [];
angular.forEach(userList, function (user, index) { angular.forEach(userList, function (user, index) {
content.push(creadeUserHeadLine(user)); content.push(createUserHeadLine(user));
content.push(createAccessDataContent(user)); content.push(createAccessDataContent(user));
content.push(createWelcomeText()); content.push(createWelcomeText());
// No pagebreak after the last user // No pagebreak after the last user

View File

@ -355,7 +355,9 @@
<div os-perms="users.can_manage" ng-class="{'hiddenDiv': !user.hover}"> <div os-perms="users.can_manage" ng-class="{'hiddenDiv': !user.hover}">
<small> <small>
<template-hook hook-name="userListEditButton"> <template-hook hook-name="userListEditButton">
<span>
<a href="" ng-click="openDialog(user)" translate>Edit</a> &middot; <a href="" ng-click="openDialog(user)" translate>Edit</a> &middot;
</span>
</template-hook> </template-hook>
<a ui-sref="users.user.change-password({id: user.id})" translate>Change password</a> &middot; <a ui-sref="users.user.change-password({id: user.id})" translate>Change password</a> &middot;
<a href="" class="text-danger" <a href="" class="text-danger"