Merge pull request #1777 from emanuelschuetze/fix1662

Sort list of speakers (Fixes#1662)
This commit is contained in:
Oskar Hahn 2016-01-09 12:04:38 +01:00
commit db22d1f7dc
3 changed files with 84 additions and 16 deletions

View File

@ -181,16 +181,22 @@ angular.module('OpenSlidesApp.agenda.site', ['OpenSlidesApp.agenda'])
.controller('ItemDetailCtrl', [ .controller('ItemDetailCtrl', [
'$scope', '$scope',
'$filter',
'$http', '$http',
'Agenda', 'Agenda',
'User', 'User',
'item', 'item',
function ($scope, $http, Agenda, User, item) { function ($scope, $filter, $http, Agenda, User, item) {
Agenda.bindOne(item.id, $scope, 'item'); Agenda.bindOne(item.id, $scope, 'item');
User.bindAll({}, $scope, 'users'); User.bindAll({}, $scope, 'users');
$scope.speaker = {}; $scope.speaker = {};
$scope.alert = {}; $scope.alert = {};
$scope.speakers = $filter('orderBy')(item.speakers, 'weight');
$scope.$watch(function () {
return Agenda.lastModified();
}, function () {
$scope.speakers = $filter('orderBy')(item.speakers, 'weight');
});
// close/open list of speakers of current item // close/open list of speakers of current item
$scope.closeList = function (listClosed) { $scope.closeList = function (listClosed) {
item.speaker_list_closed = listClosed; item.speaker_list_closed = listClosed;
@ -201,6 +207,7 @@ angular.module('OpenSlidesApp.agenda.site', ['OpenSlidesApp.agenda'])
$http.post('/rest/agenda/item/' + item.id + '/manage_speaker/', {'user': userId}) $http.post('/rest/agenda/item/' + item.id + '/manage_speaker/', {'user': userId})
.success(function(data){ .success(function(data){
$scope.alert.show = false; $scope.alert.show = false;
$scope.speakers = item.speakers;
}) })
.error(function(data){ .error(function(data){
$scope.alert = { type: 'danger', msg: data.detail, show: true }; $scope.alert = { type: 'danger', msg: data.detail, show: true };
@ -211,9 +218,13 @@ angular.module('OpenSlidesApp.agenda.site', ['OpenSlidesApp.agenda'])
$http.delete('/rest/agenda/item/' + item.id + '/manage_speaker/', $http.delete('/rest/agenda/item/' + item.id + '/manage_speaker/',
{headers: {'Content-Type': 'application/json'}, {headers: {'Content-Type': 'application/json'},
data: JSON.stringify({speaker: speakerId})}) data: JSON.stringify({speaker: speakerId})})
.success(function(data){
$scope.speakers = item.speakers;
})
.error(function(data){ .error(function(data){
$scope.alert = { type: 'danger', msg: data.detail, show: true }; $scope.alert = { type: 'danger', msg: data.detail, show: true };
}); });
$scope.speakers = item.speakers;
}; };
// begin speech of selected/next speaker // begin speech of selected/next speaker
$scope.beginSpeech = function (speakerId) { $scope.beginSpeech = function (speakerId) {
@ -234,6 +245,18 @@ angular.module('OpenSlidesApp.agenda.site', ['OpenSlidesApp.agenda'])
$scope.alert = { type: 'danger', msg: data.detail, show: true }; $scope.alert = { type: 'danger', msg: data.detail, show: true };
}); });
}; };
// save reordered list of speakers
$scope.treeOptions = {
dropped: function (event) {
var sortedSpeakers = [];
var nextSpeakers = $filter('filter')($scope.speakers, {'begin_time': null});
angular.forEach(nextSpeakers, function (speaker) {
sortedSpeakers.push(speaker.id);
});
$http.post('/rest/agenda/item/' + item.id + '/sort_speakers/',
{speakers: sortedSpeakers});
}
};
} }
]) ])

View File

@ -65,20 +65,26 @@
</strong> </strong>
<h3 translate>Next speakers:</h3> <h3 translate>Next speakers:</h3>
<ol> <div class="row">
<li ng-repeat="speaker in item.speakers | filter: {begin_time: null}"> <div ui-tree="treeOptions" class="col-sm-6">
{{ speaker.user.get_full_name() }} <ol ui-tree-nodes="" ng-model="speakers">
<button os-perms="agenda.can_manage" ng-click="removeSpeaker(speaker.id)" <li ng-repeat="speaker in speakers | filter: {begin_time: null}" ui-tree-node>
class="btn btn-default btn-xs"> <i ui-tree-handle="" class="fa fa-arrows-v"></i>
<i class="fa fa-times"></i> {{ $index + 1 }}.
</button> {{ speaker.user.get_full_name() }}
<button os-perms="agenda.can_manage" ng-click="beginSpeech(speaker.id)" <button os-perms="agenda.can_manage" ng-click="removeSpeaker(speaker.id)"
class="btn btn-default btn-xs"> class="btn btn-default btn-xs">
<i class="fa fa-play"></i> <i class="fa fa-times"></i>
</button> </button>
</ol> <button os-perms="agenda.can_manage" ng-click="beginSpeech(speaker.id)"
class="btn btn-default btn-xs">
<i class="fa fa-play"></i>
</button>
</ol>
</div>
</div>
<div class="form-group"> <div class="form-group spacer">
<alert ng-show="alert.show" type="{{ alert.type }}" ng-click="alert={}" close="alert={}"> <alert ng-show="alert.show" type="{{ alert.type }}" ng-click="alert={}" close="alert={}">
{{alert.msg}} {{alert.msg}}
</alert> </alert>

View File

@ -1,6 +1,7 @@
from cgi import escape from cgi import escape
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.db import transaction
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy from django.utils.translation import ugettext_lazy
from reportlab.platypus import Paragraph from reportlab.platypus import Paragraph
@ -48,7 +49,7 @@ class ItemViewSet(ListModelMixin, RetrieveModelMixin, UpdateModelMixin, GenericV
result = (self.request.user.has_perm('agenda.can_see') and result = (self.request.user.has_perm('agenda.can_see') and
self.request.user.has_perm('agenda.can_see_hidden_items') and self.request.user.has_perm('agenda.can_see_hidden_items') and
self.request.user.has_perm('agenda.can_manage')) self.request.user.has_perm('agenda.can_manage'))
elif self.action in ('speak', 'numbering'): elif self.action in ('speak', 'sort_speakers', 'numbering'):
result = (self.request.user.has_perm('agenda.can_see') and result = (self.request.user.has_perm('agenda.can_see') and
self.request.user.has_perm('agenda.can_manage')) self.request.user.has_perm('agenda.can_manage'))
else: else:
@ -195,6 +196,44 @@ class ItemViewSet(ListModelMixin, RetrieveModelMixin, UpdateModelMixin, GenericV
# Initiate response. # Initiate response.
return Response({'detail': message}) return Response({'detail': message})
@detail_route(methods=['POST'])
def sort_speakers(self, request, pk=None):
"""
Special view endpoint to sort the list of speakers.
Expects a list of IDs of the speakers.
"""
# Retrieve item.
item = self.get_object()
# Check data
speaker_ids = request.data.get('speakers')
if not isinstance(speaker_ids, list):
raise ValidationError(
{'detail': _('Invalid data.')})
# Get all speakers
speakers = {}
for speaker in item.speakers.filter(begin_time=None):
speakers[speaker.pk] = speaker
# Check and sort speakers
valid_speakers = []
for speaker_id in speaker_ids:
if not isinstance(speaker_id, int) or speakers.get(speaker_id) is None:
raise ValidationError(
{'detail': _('Invalid data.')})
valid_speakers.append(speakers[speaker_id])
weight = 0
with transaction.atomic():
for speaker in valid_speakers:
speaker.weight = weight
speaker.save()
weight += 1
# Initiate response.
return Response({'detail': _('List of speakers successfully sorted.')})
@list_route(methods=['get', 'put']) @list_route(methods=['get', 'put'])
def tree(self, request): def tree(self, request):
""" """