diff --git a/openslides/agenda/templates/agenda/speaker_widget.html b/openslides/agenda/templates/agenda/speaker_widget.html index 0e5567ec7..11a54fa25 100644 --- a/openslides/agenda/templates/agenda/speaker_widget.html +++ b/openslides/agenda/templates/agenda/speaker_widget.html @@ -9,4 +9,5 @@ {% if perms.agenda.can_manage_agenda %}
{% trans 'Next speaker' %} + {% trans 'End speach' %} {% endif %} diff --git a/openslides/agenda/urls.py b/openslides/agenda/urls.py index 705351a68..a671a6685 100644 --- a/openslides/agenda/urls.py +++ b/openslides/agenda/urls.py @@ -114,5 +114,10 @@ urlpatterns = patterns( url(r'^list_of_speakers/next/$', CurrentListOfSpeakersView.as_view(next_speaker=True), name='agenda_next_on_current_list_of_speakers', + ), + + url(r'^list_of_speakers/end_speach/$', + CurrentListOfSpeakersView.as_view(end_speach=True), + name='agenda_end_speach_on_current_list_of_speakers', ) ) diff --git a/openslides/agenda/views.py b/openslides/agenda/views.py index 8e7cb41fb..8642a8bec 100644 --- a/openslides/agenda/views.py +++ b/openslides/agenda/views.py @@ -436,11 +436,12 @@ class SpeakerChangeOrderView(SingleObjectMixin, RedirectView): class CurrentListOfSpeakersView(RedirectView): """ - Redirect to the current list of speakers and set the request.user on it or - begins speach of the next speaker. + Redirect to the current list of speakers and set the request.user on it, + begins speach of the next speaker or ends the speach of the current speaker. """ set_speaker = False next_speaker = False + end_speach = False def get_item(self): """ @@ -463,11 +464,14 @@ class CurrentListOfSpeakersView(RedirectView): in other case, it returns the URL to the dashboard. - This method also adds the request.user to the list of speakers, if he + This method also adds the request.user to the list of speakers if he has the right permissions and the list is not closed. - This method also begins the speach of the next speaker, if the flag + This method also begins the speach of the next speaker if the flag next_speaker is given. + + This method also ends the speach of the current speaker if the flag + end_speach is given. """ item = self.get_item() request = self.request @@ -506,6 +510,16 @@ class CurrentListOfSpeakersView(RedirectView): if not self.set_speaker: reverse_to_dashboard = True + if self.end_speach: + try: + current_speaker = item.speaker_set.filter(end_time=None).exclude(begin_time=None).get() + except Speaker.DoesNotExist: + messages.error(request, _('There is no one speaking at the moment.')) + else: + current_speaker.end_speach() + messages.success(request, _('%s is now finished.') % current_speaker) + reverse_to_dashboard = True + if reverse_to_dashboard or not self.request.user.has_perm('agenda.can_see_agenda'): return reverse('dashboard') else: diff --git a/tests/agenda/test_list_of_speakers.py b/tests/agenda/test_list_of_speakers.py index 8a6d82ca4..b8e93c7df 100644 --- a/tests/agenda/test_list_of_speakers.py +++ b/tests/agenda/test_list_of_speakers.py @@ -13,6 +13,7 @@ from openslides.utils.exceptions import OpenSlidesError from openslides.utils.test import TestCase from openslides.participant.models import User from openslides.agenda.models import Item, Speaker +from openslides.config.api import config class ListOfSpeakerModelTests(TestCase): @@ -200,3 +201,70 @@ class SpeakerListOpenView(SpeakerViewTestCase): response = self.check_url('/agenda/1/speaker/reopen/', self.admin_client, 302) item = Item.objects.get(pk=self.item1.pk) self.assertFalse(item.speaker_list_closed) + + +class GlobalListOfSpeakersLinks(SpeakerViewTestCase): + def test_global_redirect_url(self): + self.assertFalse(config['presentation']) + response = self.speaker1_client.get('/agenda/list_of_speakers/') + self.assertRedirects(response, '/projector/dashboard/') + self.assertMessage(response, 'There is no list of speakers for the current slide. Please choose the agenda item manually from the agenda.') + + response = self.admin_client.get('/projector/activate/item-1/') + self.assertEqual(config['presentation'], 'item-1') + response = self.speaker1_client.get('/agenda/list_of_speakers/') + self.assertRedirects(response, '/agenda/1/') + + def test_global_add_url(self): + self.assertFalse(config['presentation']) + response = self.speaker1_client.get('/agenda/list_of_speakers/add/') + self.assertRedirects(response, '/projector/dashboard/') + self.assertMessage(response, 'There is no list of speakers for the current slide. Please choose the agenda item manually from the agenda.') + + response = self.admin_client.get('/projector/activate/item-1/') + self.assertEqual(config['presentation'], 'item-1') + response = self.speaker1_client.get('/agenda/list_of_speakers/add/') + self.assertRedirects(response, '/agenda/1/') + self.assertEqual(Speaker.objects.get(item__pk='1').person, self.speaker1) + + def test_global_next_speaker_url(self): + self.assertFalse(config['presentation']) + response = self.admin_client.get('/agenda/list_of_speakers/next/') + self.assertRedirects(response, '/projector/dashboard/') + self.assertMessage(response, 'There is no list of speakers for the current slide. Please choose the agenda item manually from the agenda.') + + response = self.admin_client.get('/projector/activate/item-1/') + self.assertEqual(config['presentation'], 'item-1') + response = self.admin_client.get('/agenda/list_of_speakers/next/') + self.assertRedirects(response, '/projector/dashboard/') + self.assertMessage(response, 'The list of speakers is empty.') + + response = self.speaker1_client.get('/agenda/list_of_speakers/add/') + self.assertTrue(Speaker.objects.get(item__pk='1').begin_time is None) + response = self.admin_client.get('/agenda/list_of_speakers/next/') + self.assertRedirects(response, '/projector/dashboard/') + self.assertTrue(Speaker.objects.get(item__pk='1').begin_time is not None) + + def test_global_end_speach_url(self): + self.assertFalse(config['presentation']) + response = self.admin_client.get('/agenda/list_of_speakers/end_speach/') + self.assertRedirects(response, '/projector/dashboard/') + self.assertMessage(response, 'There is no list of speakers for the current slide. Please choose the agenda item manually from the agenda.') + + response = self.admin_client.get('/projector/activate/item-1/') + self.assertEqual(config['presentation'], 'item-1') + response = self.admin_client.get('/agenda/list_of_speakers/end_speach/') + self.assertRedirects(response, '/projector/dashboard/') + self.assertMessage(response, 'There is no one speaking at the moment.') + + response = self.speaker1_client.get('/agenda/list_of_speakers/add/') + self.assertTrue(Speaker.objects.get(item__pk='1').begin_time is None) + response = self.admin_client.get('/agenda/list_of_speakers/end_speach/') + self.assertRedirects(response, '/projector/dashboard/') + self.assertMessage(response, 'There is no one speaking at the moment.') + + response = self.admin_client.get('/agenda/list_of_speakers/next/') + self.assertTrue(Speaker.objects.get(item__pk='1').end_time is None) + response = self.admin_client.get('/agenda/list_of_speakers/end_speach/') + self.assertRedirects(response, '/projector/dashboard/') + self.assertTrue(Speaker.objects.get(item__pk='1').end_time is not None)