diff --git a/openslides/agenda/static/js/agenda/site.js b/openslides/agenda/static/js/agenda/site.js index 9452d97fc..fc806061d 100644 --- a/openslides/agenda/static/js/agenda/site.js +++ b/openslides/agenda/static/js/agenda/site.js @@ -181,16 +181,22 @@ angular.module('OpenSlidesApp.agenda.site', ['OpenSlidesApp.agenda']) .controller('ItemDetailCtrl', [ '$scope', + '$filter', '$http', 'Agenda', 'User', 'item', - function ($scope, $http, Agenda, User, item) { + function ($scope, $filter, $http, Agenda, User, item) { Agenda.bindOne(item.id, $scope, 'item'); User.bindAll({}, $scope, 'users'); $scope.speaker = {}; $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 $scope.closeList = function (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}) .success(function(data){ $scope.alert.show = false; + $scope.speakers = item.speakers; }) .error(function(data){ $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/', {headers: {'Content-Type': 'application/json'}, data: JSON.stringify({speaker: speakerId})}) + .success(function(data){ + $scope.speakers = item.speakers; + }) .error(function(data){ $scope.alert = { type: 'danger', msg: data.detail, show: true }; }); + $scope.speakers = item.speakers; }; // begin speech of selected/next speaker $scope.beginSpeech = function (speakerId) { @@ -234,6 +245,18 @@ angular.module('OpenSlidesApp.agenda.site', ['OpenSlidesApp.agenda']) $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}); + } + }; } ]) diff --git a/openslides/agenda/static/templates/agenda/item-detail.html b/openslides/agenda/static/templates/agenda/item-detail.html index 95e23b4b3..f637ce744 100644 --- a/openslides/agenda/static/templates/agenda/item-detail.html +++ b/openslides/agenda/static/templates/agenda/item-detail.html @@ -65,20 +65,26 @@

Next speakers:

-
    -
  1. - {{ speaker.user.get_full_name() }} - - -
+
+
+
    +
  1. + + {{ $index + 1 }}. + {{ speaker.user.get_full_name() }} + + +
+
+
-
+
{{alert.msg}} diff --git a/openslides/agenda/views.py b/openslides/agenda/views.py index 2202ae970..b71c5d44f 100644 --- a/openslides/agenda/views.py +++ b/openslides/agenda/views.py @@ -1,6 +1,7 @@ from cgi import escape 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_lazy 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 self.request.user.has_perm('agenda.can_see_hidden_items') and 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 self.request.user.has_perm('agenda.can_manage')) else: @@ -195,6 +196,44 @@ class ItemViewSet(ListModelMixin, RetrieveModelMixin, UpdateModelMixin, GenericV # Initiate response. 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']) def tree(self, request): """