List of speakers. Part 2
This commit is contained in:
parent
1a416e5726
commit
33f74c3025
@ -61,6 +61,9 @@ class ItemOrderForm(CssClassMixin, forms.Form):
|
|||||||
|
|
||||||
|
|
||||||
class AppendSpeakerForm(CssClassMixin, forms.Form):
|
class AppendSpeakerForm(CssClassMixin, forms.Form):
|
||||||
|
"""
|
||||||
|
Form to set an user to a list of speakers.
|
||||||
|
"""
|
||||||
speaker = PersonFormField(
|
speaker = PersonFormField(
|
||||||
widget=forms.Select(attrs={'class': 'medium-input'}),
|
widget=forms.Select(attrs={'class': 'medium-input'}),
|
||||||
label=ugettext_lazy("Add participant"))
|
label=ugettext_lazy("Add participant"))
|
||||||
@ -70,6 +73,9 @@ class AppendSpeakerForm(CssClassMixin, forms.Form):
|
|||||||
return super(AppendSpeakerForm, self).__init__(*args, **kwargs)
|
return super(AppendSpeakerForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def clean_speaker(self):
|
def clean_speaker(self):
|
||||||
|
"""
|
||||||
|
Checks, that the user is not already on the list.
|
||||||
|
"""
|
||||||
speaker = self.cleaned_data['speaker']
|
speaker = self.cleaned_data['speaker']
|
||||||
if Speaker.objects.filter(person=speaker, item=self.item, time=None).exists():
|
if Speaker.objects.filter(person=speaker, item=self.item, time=None).exists():
|
||||||
raise forms.ValidationError(ugettext_lazy(
|
raise forms.ValidationError(ugettext_lazy(
|
||||||
|
@ -42,23 +42,35 @@ class Item(MPTTModel, SlideMixin):
|
|||||||
(ORGANIZATIONAL_ITEM, _('Organizational item')))
|
(ORGANIZATIONAL_ITEM, _('Organizational item')))
|
||||||
|
|
||||||
title = models.CharField(null=True, max_length=255, verbose_name=_("Title"))
|
title = models.CharField(null=True, max_length=255, verbose_name=_("Title"))
|
||||||
"""Title of the agenda item."""
|
"""
|
||||||
|
Title of the agenda item.
|
||||||
|
"""
|
||||||
|
|
||||||
text = models.TextField(null=True, blank=True, verbose_name=_("Text"))
|
text = models.TextField(null=True, blank=True, verbose_name=_("Text"))
|
||||||
"""The optional text of the agenda item."""
|
"""
|
||||||
|
The optional text of the agenda item.
|
||||||
|
"""
|
||||||
|
|
||||||
comment = models.TextField(null=True, blank=True, verbose_name=_("Comment"))
|
comment = models.TextField(null=True, blank=True, verbose_name=_("Comment"))
|
||||||
"""Optional comment to the agenda item. Will not be shoun to normal users."""
|
"""
|
||||||
|
Optional comment to the agenda item. Will not be shoun to normal users.
|
||||||
|
"""
|
||||||
|
|
||||||
closed = models.BooleanField(default=False, verbose_name=_("Closed"))
|
closed = models.BooleanField(default=False, verbose_name=_("Closed"))
|
||||||
"""Flag, if the item is finished."""
|
"""
|
||||||
|
Flag, if the item is finished.
|
||||||
|
"""
|
||||||
|
|
||||||
weight = models.IntegerField(default=0, verbose_name=_("Weight"))
|
weight = models.IntegerField(default=0, verbose_name=_("Weight"))
|
||||||
"""Weight to sort the item in the agenda."""
|
"""
|
||||||
|
Weight to sort the item in the agenda.
|
||||||
|
"""
|
||||||
|
|
||||||
parent = TreeForeignKey('self', null=True, blank=True,
|
parent = TreeForeignKey('self', null=True, blank=True,
|
||||||
related_name='children')
|
related_name='children')
|
||||||
"""The parent item in the agenda tree."""
|
"""
|
||||||
|
The parent item in the agenda tree.
|
||||||
|
"""
|
||||||
|
|
||||||
type = models.IntegerField(max_length=1, choices=ITEM_TYPE,
|
type = models.IntegerField(max_length=1, choices=ITEM_TYPE,
|
||||||
default=AGENDA_ITEM, verbose_name=_("Type"))
|
default=AGENDA_ITEM, verbose_name=_("Type"))
|
||||||
@ -70,7 +82,9 @@ class Item(MPTTModel, SlideMixin):
|
|||||||
|
|
||||||
duration = models.CharField(null=True, blank=True, max_length=5,
|
duration = models.CharField(null=True, blank=True, max_length=5,
|
||||||
verbose_name=_("Duration (hh:mm)"))
|
verbose_name=_("Duration (hh:mm)"))
|
||||||
"""The intended duration for the topic."""
|
"""
|
||||||
|
The intended duration for the topic.
|
||||||
|
"""
|
||||||
|
|
||||||
related_sid = models.CharField(null=True, blank=True, max_length=63)
|
related_sid = models.CharField(null=True, blank=True, max_length=63)
|
||||||
"""
|
"""
|
||||||
@ -230,7 +244,7 @@ class Item(MPTTModel, SlideMixin):
|
|||||||
class SpeakerManager(models.Manager):
|
class SpeakerManager(models.Manager):
|
||||||
def add(self, person, item):
|
def add(self, person, item):
|
||||||
if self.filter(person=person, item=item, time=None).exists():
|
if self.filter(person=person, item=item, time=None).exists():
|
||||||
raise OpenSlidesError(_('%s is allready on the list of speakers from item %d') % (person, item.id))
|
raise OpenSlidesError(_('%s is already on the list of speakers of item %d.') % (person, item.id))
|
||||||
weight = (self.filter(item=item).aggregate(
|
weight = (self.filter(item=item).aggregate(
|
||||||
models.Max('weight'))['weight__max'] or 0)
|
models.Max('weight'))['weight__max'] or 0)
|
||||||
return self.create(item=item, person=person, weight=weight + 1)
|
return self.create(item=item, person=person, weight=weight + 1)
|
||||||
@ -244,9 +258,24 @@ class Speaker(models.Model):
|
|||||||
objects = SpeakerManager()
|
objects = SpeakerManager()
|
||||||
|
|
||||||
person = PersonField()
|
person = PersonField()
|
||||||
|
"""
|
||||||
|
ForeinKey to the person who speaks.
|
||||||
|
"""
|
||||||
|
|
||||||
item = models.ForeignKey(Item)
|
item = models.ForeignKey(Item)
|
||||||
time = models.TimeField(null=True)
|
"""
|
||||||
|
ForeinKey to the AgendaItem to which the person want to speak.
|
||||||
|
"""
|
||||||
|
|
||||||
|
time = models.DateTimeField(null=True)
|
||||||
|
"""
|
||||||
|
Saves the time, when the speaker has spoken. None, if he has not spoken yet.
|
||||||
|
"""
|
||||||
|
|
||||||
weight = models.IntegerField(null=True)
|
weight = models.IntegerField(null=True)
|
||||||
|
"""
|
||||||
|
The sort order of the list of speakers. None, if he has already spoken.
|
||||||
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
permissions = (
|
permissions = (
|
||||||
@ -264,6 +293,11 @@ class Speaker(models.Model):
|
|||||||
args=[self.item.pk, self.pk])
|
args=[self.item.pk, self.pk])
|
||||||
|
|
||||||
def speak(self):
|
def speak(self):
|
||||||
|
"""
|
||||||
|
Let the person speak.
|
||||||
|
|
||||||
|
Set the weight to None and the time to now.
|
||||||
|
"""
|
||||||
self.weight = None
|
self.weight = None
|
||||||
self.time = datetime.now()
|
self.time = datetime.now()
|
||||||
self.save()
|
self.save()
|
||||||
|
@ -13,10 +13,17 @@
|
|||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext_lazy, ugettext_noop, ugettext as _
|
from django.utils.translation import ugettext_lazy, ugettext_noop, ugettext as _
|
||||||
|
from django.template.loader import render_to_string
|
||||||
|
|
||||||
from openslides.config.signals import config_signal
|
from openslides.config.signals import config_signal
|
||||||
from openslides.config.api import ConfigVariable, ConfigPage
|
from openslides.config.api import ConfigVariable, ConfigPage
|
||||||
|
|
||||||
|
from openslides.projector.signals import projector_overlays
|
||||||
|
from openslides.projector.projector import Overlay
|
||||||
|
from openslides.projector.api import get_active_slide, get_slide_from_sid
|
||||||
|
|
||||||
|
from .models import Speaker, Item
|
||||||
|
|
||||||
|
|
||||||
# TODO: Reinsert the datepicker scripts in the template
|
# TODO: Reinsert the datepicker scripts in the template
|
||||||
|
|
||||||
@ -48,3 +55,31 @@ def setup_agenda_config_page(sender, **kwargs):
|
|||||||
variables=(agenda_start_event_date_time,),
|
variables=(agenda_start_event_date_time,),
|
||||||
extra_context={'extra_stylefiles': extra_stylefiles,
|
extra_context={'extra_stylefiles': extra_stylefiles,
|
||||||
'extra_javascript': extra_javascript})
|
'extra_javascript': extra_javascript})
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(projector_overlays, dispatch_uid="agenda_list_of_speakers")
|
||||||
|
def agenda_list_of_speakers(sender, **kwargs):
|
||||||
|
"""
|
||||||
|
Receiver for the list of speaker overlay.
|
||||||
|
"""
|
||||||
|
name = 'agenda_speaker'
|
||||||
|
|
||||||
|
def get_widget_html():
|
||||||
|
"""
|
||||||
|
Returns the the html-code to show in the overly-widget.
|
||||||
|
"""
|
||||||
|
return render_to_string('agenda/overlay_speaker_widget.html')
|
||||||
|
|
||||||
|
def get_projector_html():
|
||||||
|
"""
|
||||||
|
Returns an html-code to show on the projector.
|
||||||
|
"""
|
||||||
|
slide = get_slide_from_sid(get_active_slide(only_sid=True), element=True)
|
||||||
|
if not isinstance(slide, Item):
|
||||||
|
# Only show list of speakers on Agenda-Items
|
||||||
|
return None
|
||||||
|
speakers = Speaker.objects.filter(time=None, item=slide)[:5]
|
||||||
|
context = {'speakers': speakers}
|
||||||
|
return render_to_string('agenda/overlay_speaker_projector.html', context)
|
||||||
|
|
||||||
|
return Overlay(name, get_widget_html, get_projector_html)
|
||||||
|
@ -25,3 +25,13 @@ table#agendatime td {
|
|||||||
#list_of_speakers li {
|
#list_of_speakers li {
|
||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#list_of_speakers {
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#list_of_speakers span.ui-icon {
|
||||||
|
position: absolute;
|
||||||
|
margin-left: -15px;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
<style type="text/css">
|
||||||
|
#overlay_speaker_inner {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
border-radius: 0.2em;
|
||||||
|
background-color: #cccccc;
|
||||||
|
opacity: 0.6;
|
||||||
|
padding: 0.2em 0;
|
||||||
|
margin: 1em;
|
||||||
|
z-index:2;
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div id="overlay_speaker_inner">
|
||||||
|
{% if speakers %}
|
||||||
|
<header>{% trans "List of speakers:" %}</header>
|
||||||
|
<ol>
|
||||||
|
{% for speaker in speakers %}
|
||||||
|
<li>{{ speaker }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ol>
|
||||||
|
{% else %}
|
||||||
|
<i>{% trans 'The list of speakers is empty.' %}</i>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
@ -0,0 +1,6 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
{% load tags %}
|
||||||
|
|
||||||
|
<span>
|
||||||
|
{% trans "List of speakers" %}
|
||||||
|
</span>
|
6
openslides/agenda/templates/agenda/speaker_widget.html
Normal file
6
openslides/agenda/templates/agenda/speaker_widget.html
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
{% load tags %}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<a href="{% url 'agenda_current_list_of_speakers' %}">{% trans 'Put me on the current list of speakers' %}</a>
|
||||||
|
</div>
|
@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
{% block header %}
|
{% block header %}
|
||||||
<link type="text/css" rel="stylesheet" media="all" href="{% static 'styles/agenda.css' %}" />
|
<link type="text/css" rel="stylesheet" media="all" href="{% static 'styles/agenda.css' %}" />
|
||||||
|
{% comment %} TODO: include stylesheet in our repo{% endcomment %}
|
||||||
|
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.2/themes/smoothness/jquery-ui.css" />
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block javascript %}
|
{% block javascript %}
|
||||||
@ -59,7 +61,7 @@
|
|||||||
<p>
|
<p>
|
||||||
{% if perms.agenda.can_manage_agenda %}
|
{% if perms.agenda.can_manage_agenda %}
|
||||||
{% if item.speaker_list_closed %}
|
{% if item.speaker_list_closed %}
|
||||||
<a href="{% url 'agenda_speaker_open' item.pk %}" class="btn btn-mini btn-danger">{% trans 'Open list' %}</a>
|
<a href="{% url 'agenda_speaker_reopen' item.pk %}" class="btn btn-mini btn-danger">{% trans 'Open list' %}</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{% url 'agenda_speaker_close' item.pk %}" class="btn btn-mini btn-danger">{% trans 'Close list' %}</a>
|
<a href="{% url 'agenda_speaker_close' item.pk %}" class="btn btn-mini btn-danger">{% trans 'Close list' %}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -130,9 +132,11 @@
|
|||||||
|
|
||||||
<div class="well">
|
<div class="well">
|
||||||
<b>{% trans "Next speakers:" %}</b>
|
<b>{% trans "Next speakers:" %}</b>
|
||||||
<ol {% if perms.agenda.can_manage_agenda %}id="list_of_speakers"{% endif %}>
|
<ul {% if perms.agenda.can_manage_agenda %}id="list_of_speakers"{% endif %}>
|
||||||
{% for speaker in speakers %}
|
{% for speaker in speakers %}
|
||||||
<li id="speaker_{{ speaker.pk }}">
|
<li id="speaker_{{ speaker.pk }}">
|
||||||
|
<span {% if perms.agenda.can_manage_agenda %}class="ui-icon ui-icon-arrowthick-2-n-s"{% endif %}></span>
|
||||||
|
<span>{{ forloop.counter }}.</span>
|
||||||
<a href="{% model_url speaker %}">{{ speaker }}</a>
|
<a href="{% model_url speaker %}">{{ speaker }}</a>
|
||||||
{% if perms.agenda.can_manage_agenda %}
|
{% if perms.agenda.can_manage_agenda %}
|
||||||
<a href="{% url 'agenda_speaker_speak' item.pk speaker.person.person_id %}" class="btn btn-mini"><i class="icon-bell"></i> Finished speech</a>
|
<a href="{% url 'agenda_speaker_speak' item.pk speaker.person.person_id %}" class="btn btn-mini"><i class="icon-bell"></i> Finished speech</a>
|
||||||
@ -144,7 +148,7 @@
|
|||||||
{% empty %}
|
{% empty %}
|
||||||
<i>{% trans "The list of speakers is empty." %}</i>
|
<i>{% trans "The list of speakers is empty." %}</i>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ol>
|
</ul>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
{% if is_speaker %}
|
{% if is_speaker %}
|
||||||
|
@ -4,6 +4,16 @@
|
|||||||
|
|
||||||
{% block title %}{{ block.super }} - {{ item }}{% endblock %}
|
{% block title %}{{ block.super }} - {{ item }}{% endblock %}
|
||||||
|
|
||||||
|
{% block header %}
|
||||||
|
<style type="text/css">
|
||||||
|
#list_of_speakers li
|
||||||
|
{
|
||||||
|
font-size: 130%;
|
||||||
|
line-height: 160%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>{{ title }}</h1>
|
<h1>{{ title }}</h1>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -14,7 +14,7 @@ from django.conf.urls import url, patterns
|
|||||||
from openslides.agenda.views import (
|
from openslides.agenda.views import (
|
||||||
Overview, AgendaItemView, SetClosed, ItemUpdate, SpeakerSpeakView,
|
Overview, AgendaItemView, SetClosed, ItemUpdate, SpeakerSpeakView,
|
||||||
ItemCreate, ItemDelete, AgendaPDF, SpeakerAppendView, SpeakerDeleteView,
|
ItemCreate, ItemDelete, AgendaPDF, SpeakerAppendView, SpeakerDeleteView,
|
||||||
SpeakerListOpenView, SpeakerChangeOrderView)
|
SpeakerListCloseView, SpeakerChangeOrderView, CurrentListOfSpeakersView)
|
||||||
|
|
||||||
urlpatterns = patterns(
|
urlpatterns = patterns(
|
||||||
'',
|
'',
|
||||||
@ -66,14 +66,14 @@ urlpatterns = patterns(
|
|||||||
name='agenda_speaker_append',
|
name='agenda_speaker_append',
|
||||||
),
|
),
|
||||||
|
|
||||||
url(r'^(?P<pk>\d+)/speaker/open/$',
|
url(r'^(?P<pk>\d+)/speaker/close/$',
|
||||||
SpeakerListOpenView.as_view(open_list=True),
|
SpeakerListCloseView.as_view(),
|
||||||
name='agenda_speaker_open',
|
name='agenda_speaker_close',
|
||||||
),
|
),
|
||||||
|
|
||||||
url(r'^(?P<pk>\d+)/speaker/close/$',
|
url(r'^(?P<pk>\d+)/speaker/reopen/$',
|
||||||
SpeakerListOpenView.as_view(),
|
SpeakerListCloseView.as_view(reopen=True),
|
||||||
name='agenda_speaker_close',
|
name='agenda_speaker_reopen',
|
||||||
),
|
),
|
||||||
|
|
||||||
url(r'^(?P<pk>\d+)/speaker/del/$',
|
url(r'^(?P<pk>\d+)/speaker/del/$',
|
||||||
@ -91,8 +91,13 @@ urlpatterns = patterns(
|
|||||||
name='agenda_speaker_speak',
|
name='agenda_speaker_speak',
|
||||||
),
|
),
|
||||||
|
|
||||||
url(r'^(?P<pk>\d+)/speaker/change_order$',
|
url(r'^(?P<pk>\d+)/speaker/change_order/$',
|
||||||
SpeakerChangeOrderView.as_view(),
|
SpeakerChangeOrderView.as_view(),
|
||||||
name='agenda_speaker_change_order',
|
name='agenda_speaker_change_order',
|
||||||
),
|
),
|
||||||
|
|
||||||
|
url(r'^list_of_speakers/$',
|
||||||
|
CurrentListOfSpeakersView.as_view(),
|
||||||
|
name='agenda_current_list_of_speakers',
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
@ -29,7 +29,7 @@ from openslides.utils.views import (
|
|||||||
DetailView, FormView, SingleObjectMixin)
|
DetailView, FormView, SingleObjectMixin)
|
||||||
from openslides.utils.template import Tab
|
from openslides.utils.template import Tab
|
||||||
from openslides.utils.utils import html_strong
|
from openslides.utils.utils import html_strong
|
||||||
from openslides.projector.api import get_active_slide
|
from openslides.projector.api import get_active_slide, get_slide_from_sid
|
||||||
from openslides.projector.projector import Widget, SLIDE
|
from openslides.projector.projector import Widget, SLIDE
|
||||||
from .models import Item, Speaker
|
from .models import Item, Speaker
|
||||||
from .forms import ItemOrderForm, ItemForm, AppendSpeakerForm
|
from .forms import ItemOrderForm, ItemForm, AppendSpeakerForm
|
||||||
@ -128,7 +128,8 @@ class AgendaItemView(SingleObjectMixin, FormView):
|
|||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
speakers = Speaker.objects.filter(time=None, item=self.object.pk).order_by('weight')
|
speakers = Speaker.objects.filter(time=None, item=self.object.pk).order_by('weight')
|
||||||
old_speakers = list(Speaker.objects.exclude(time=None).order_by('time'))
|
old_speakers = list(Speaker.objects.filter(item=self.object.pk)
|
||||||
|
.exclude(time=None).order_by('time'))
|
||||||
kwargs.update({
|
kwargs.update({
|
||||||
'object': self.object,
|
'object': self.object,
|
||||||
'speakers': speakers,
|
'speakers': speakers,
|
||||||
@ -258,7 +259,6 @@ class SpeakerAppendView(SingleObjectMixin, RedirectView):
|
|||||||
"""
|
"""
|
||||||
Set the request.user to the speaker list.
|
Set the request.user to the speaker list.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
permission_required = 'agenda.can_be_speaker'
|
permission_required = 'agenda.can_be_speaker'
|
||||||
url_name = 'item_view'
|
url_name = 'item_view'
|
||||||
model = Item
|
model = Item
|
||||||
@ -278,7 +278,6 @@ class SpeakerDeleteView(DeleteView):
|
|||||||
"""
|
"""
|
||||||
Delete the request.user or a specific user from the speaker list.
|
Delete the request.user or a specific user from the speaker list.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
success_url_name = 'item_view'
|
success_url_name = 'item_view'
|
||||||
question_url_name = 'item_view'
|
question_url_name = 'item_view'
|
||||||
|
|
||||||
@ -324,7 +323,7 @@ class SpeakerDeleteView(DeleteView):
|
|||||||
|
|
||||||
class SpeakerSpeakView(SingleObjectMixin, RedirectView):
|
class SpeakerSpeakView(SingleObjectMixin, RedirectView):
|
||||||
"""
|
"""
|
||||||
Mark a speaker, that he can speak.
|
Mark the speaking person.
|
||||||
"""
|
"""
|
||||||
permission_required = 'agenda.can_manage_agenda'
|
permission_required = 'agenda.can_manage_agenda'
|
||||||
url_name = 'item_view'
|
url_name = 'item_view'
|
||||||
@ -347,18 +346,18 @@ class SpeakerSpeakView(SingleObjectMixin, RedirectView):
|
|||||||
return [self.object.pk]
|
return [self.object.pk]
|
||||||
|
|
||||||
|
|
||||||
class SpeakerListOpenView(SingleObjectMixin, RedirectView):
|
class SpeakerListCloseView(SingleObjectMixin, RedirectView):
|
||||||
"""
|
"""
|
||||||
View to open and close a list of speakers.
|
View to close and reopen a list of speakers.
|
||||||
"""
|
"""
|
||||||
permission_required = 'agenda.can_manage_agenda'
|
permission_required = 'agenda.can_manage_agenda'
|
||||||
model = Item
|
model = Item
|
||||||
open_list = False
|
reopen = False
|
||||||
url_name = 'item_view'
|
url_name = 'item_view'
|
||||||
|
|
||||||
def pre_redirect(self, *args, **kwargs):
|
def pre_redirect(self, *args, **kwargs):
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
self.object.speaker_list_closed = not self.open_list
|
self.object.speaker_list_closed = not self.reopen
|
||||||
self.object.save()
|
self.object.save()
|
||||||
|
|
||||||
def get_url_name_args(self):
|
def get_url_name_args(self):
|
||||||
@ -402,11 +401,63 @@ class SpeakerChangeOrderView(SingleObjectMixin, RedirectView):
|
|||||||
speaker.save()
|
speaker.save()
|
||||||
else:
|
else:
|
||||||
transaction.commit()
|
transaction.commit()
|
||||||
|
return None
|
||||||
|
messages.error(request, _('Could not change order. Invalid data.'))
|
||||||
|
|
||||||
def get_url_name_args(self):
|
def get_url_name_args(self):
|
||||||
return [self.object.pk]
|
return [self.object.pk]
|
||||||
|
|
||||||
|
|
||||||
|
class CurrentListOfSpeakersView(RedirectView):
|
||||||
|
"""
|
||||||
|
Redirect to the current list of speakers and set the request.user on it.
|
||||||
|
"""
|
||||||
|
def get_item(self):
|
||||||
|
"""
|
||||||
|
Returns the current Item, or None, if the current Slide is not an Agenda Item.
|
||||||
|
"""
|
||||||
|
slide = get_slide_from_sid(get_active_slide(only_sid=True), element=True)
|
||||||
|
if not isinstance(slide, Item):
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return slide
|
||||||
|
|
||||||
|
def get_redirect_url(self):
|
||||||
|
"""
|
||||||
|
Returns the URL to the item_view if:
|
||||||
|
|
||||||
|
* the current slide is an item and
|
||||||
|
* the user has the permission to see the item
|
||||||
|
|
||||||
|
in other case, it returns the URL to the dashboard.
|
||||||
|
|
||||||
|
This method also add the request.user to the list of speakers, if he
|
||||||
|
has the right permissions.
|
||||||
|
"""
|
||||||
|
item = self.get_item()
|
||||||
|
request = self.request
|
||||||
|
if item is None:
|
||||||
|
messages.error(request, _(
|
||||||
|
'There is no list of speakers for the current slide. '
|
||||||
|
'Please choose your agenda item manually from the agenda.'))
|
||||||
|
return reverse('dashboard')
|
||||||
|
|
||||||
|
if self.request.user.has_perm('agenda.can_be_speaker'):
|
||||||
|
try:
|
||||||
|
Speaker.objects.add(self.request.user, item)
|
||||||
|
except OpenSlidesError:
|
||||||
|
messages.error(request, _('You are already on the list of speakers.'))
|
||||||
|
else:
|
||||||
|
messages.success(request, _('You are now on the list of speakers.'))
|
||||||
|
else:
|
||||||
|
messages.error(request, _('You can not put yourself on the list of speakers.'))
|
||||||
|
|
||||||
|
if not self.request.user.has_perm('agenda.can_see_agenda'):
|
||||||
|
return reverse('dashboard')
|
||||||
|
else:
|
||||||
|
return reverse('item_view', args=[item.pk])
|
||||||
|
|
||||||
|
|
||||||
def register_tab(request):
|
def register_tab(request):
|
||||||
"""
|
"""
|
||||||
Registers the agenda tab.
|
Registers the agenda tab.
|
||||||
@ -425,11 +476,19 @@ def get_widgets(request):
|
|||||||
"""
|
"""
|
||||||
Returns the agenda widget for the projector tab.
|
Returns the agenda widget for the projector tab.
|
||||||
"""
|
"""
|
||||||
return [Widget(
|
return [
|
||||||
|
Widget(
|
||||||
name='agenda',
|
name='agenda',
|
||||||
display_name=_('Agenda'),
|
display_name=_('Agenda'),
|
||||||
template='agenda/widget.html',
|
template='agenda/widget.html',
|
||||||
context={
|
context={
|
||||||
'agenda': SLIDE['agenda'],
|
'agenda': SLIDE['agenda'],
|
||||||
'items': Item.objects.all()},
|
'items': Item.objects.all()},
|
||||||
permission_required='projector.can_manage_projector')]
|
permission_required='projector.can_manage_projector'),
|
||||||
|
|
||||||
|
Widget(
|
||||||
|
name='append_to_list_of_speakers',
|
||||||
|
display_name=_('To the current list of speakers'),
|
||||||
|
template='agenda/speaker_widget.html',
|
||||||
|
context={},
|
||||||
|
permission_required='agenda.can_be_speaker')]
|
||||||
|
@ -169,4 +169,4 @@ class Overlay(object):
|
|||||||
return self.name in config['projector_active_overlays']
|
return self.name in config['projector_active_overlays']
|
||||||
|
|
||||||
def show_on_projector(self):
|
def show_on_projector(self):
|
||||||
return self.is_active and self.get_projector_html() is not None
|
return self.is_active() and self.get_projector_html() is not None
|
||||||
|
@ -84,7 +84,7 @@ def setup_projector_config_variables(sender, **kwargs):
|
|||||||
@receiver(projector_overlays, dispatch_uid="projector_countdown")
|
@receiver(projector_overlays, dispatch_uid="projector_countdown")
|
||||||
def countdown(sender, **kwargs):
|
def countdown(sender, **kwargs):
|
||||||
"""
|
"""
|
||||||
Reveiver for the countdown.
|
Receiver for the countdown.
|
||||||
"""
|
"""
|
||||||
name = 'projector_countdown'
|
name = 'projector_countdown'
|
||||||
request = kwargs.get('request', None)
|
request = kwargs.get('request', None)
|
||||||
|
@ -146,12 +146,6 @@ body{
|
|||||||
color: #9FA9B7;
|
color: #9FA9B7;
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
}
|
}
|
||||||
/* list of speakers */
|
|
||||||
#list_of_speakers li
|
|
||||||
{
|
|
||||||
font-size: 130%;
|
|
||||||
line-height: 160%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Table */
|
/* Table */
|
||||||
table {
|
table {
|
||||||
|
@ -16,4 +16,4 @@
|
|||||||
|
|
||||||
<div id="overlay_message_inner">
|
<div id="overlay_message_inner">
|
||||||
{{ message }}
|
{{ message }}
|
||||||
</span>
|
</div>
|
||||||
|
@ -106,7 +106,7 @@ class TestSpeakerAppendView(SpeakerViewTestCase):
|
|||||||
# Try to set speaker 1 to item 1 again
|
# Try to set speaker 1 to item 1 again
|
||||||
response = self.check_url('/agenda/1/speaker/', self.speaker1_client, 302)
|
response = self.check_url('/agenda/1/speaker/', self.speaker1_client, 302)
|
||||||
self.assertEqual(Speaker.objects.filter(item=self.item1).count(), 1)
|
self.assertEqual(Speaker.objects.filter(item=self.item1).count(), 1)
|
||||||
self.assertMessage(response, 'speaker1 is allready on the list of speakers from item 1')
|
self.assertMessage(response, 'speaker1 is already on the list of speakers of item 1.')
|
||||||
|
|
||||||
def test_closed_list(self):
|
def test_closed_list(self):
|
||||||
self.item1.speaker_list_closed = True
|
self.item1.speaker_list_closed = True
|
||||||
@ -127,7 +127,7 @@ class TestAgendaItemView(SpeakerViewTestCase):
|
|||||||
# Try it again
|
# Try it again
|
||||||
response = self.admin_client.post(
|
response = self.admin_client.post(
|
||||||
'/agenda/1/', {'speaker': self.speaker1.person_id})
|
'/agenda/1/', {'speaker': self.speaker1.person_id})
|
||||||
self.assertFormError(response, 'form', 'speaker', 'speaker1 is allready on the list of speakers.')
|
self.assertFormError(response, 'form', 'speaker', 'speaker1 is already on the list of speakers.')
|
||||||
|
|
||||||
|
|
||||||
class TestSpeakerDeleteView(SpeakerViewTestCase):
|
class TestSpeakerDeleteView(SpeakerViewTestCase):
|
||||||
@ -170,6 +170,6 @@ class SpeakerListOpenView(SpeakerViewTestCase):
|
|||||||
item = Item.objects.get(pk=self.item1.pk)
|
item = Item.objects.get(pk=self.item1.pk)
|
||||||
self.assertTrue(item.speaker_list_closed)
|
self.assertTrue(item.speaker_list_closed)
|
||||||
|
|
||||||
response = self.check_url('/agenda/1/speaker/open/', self.admin_client, 302)
|
response = self.check_url('/agenda/1/speaker/reopen/', self.admin_client, 302)
|
||||||
item = Item.objects.get(pk=self.item1.pk)
|
item = Item.objects.get(pk=self.item1.pk)
|
||||||
self.assertFalse(item.speaker_list_closed)
|
self.assertFalse(item.speaker_list_closed)
|
||||||
|
Loading…
Reference in New Issue
Block a user