New config app. Apps only have to define config vars once. Config pages, forms and so on are created automaticly.
Changes after some reviews are done. Problematic is still that the JS can not be moved to an extra file because of the template tags in the code.
This commit is contained in:
parent
d424176def
commit
e1b149cde3
@ -10,4 +10,4 @@
|
|||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import openslides.agenda.signals
|
from . import signals
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy
|
||||||
from mptt.forms import TreeNodeChoiceField
|
from mptt.forms import TreeNodeChoiceField
|
||||||
|
|
||||||
from openslides.utils.forms import CssClassMixin
|
from openslides.utils.forms import CssClassMixin
|
||||||
@ -25,14 +25,14 @@ class ItemForm(forms.ModelForm, CssClassMixin):
|
|||||||
Form to create of update an item.
|
Form to create of update an item.
|
||||||
"""
|
"""
|
||||||
parent = TreeNodeChoiceField(
|
parent = TreeNodeChoiceField(
|
||||||
queryset=Item.objects.all(), label=_("Parent item"), required=False)
|
queryset=Item.objects.all(), label=ugettext_lazy("Parent item"), required=False)
|
||||||
|
|
||||||
duration = forms.RegexField(
|
duration = forms.RegexField(
|
||||||
regex=re.compile('[0-99]:[0-5][0-9]'),
|
regex=re.compile('[0-99]:[0-5][0-9]'),
|
||||||
error_message=_("Invalid format. Hours from 0 to 99 and minutes from 00 to 59"),
|
error_message=ugettext_lazy("Invalid format. Hours from 0 to 99 and minutes from 00 to 59"),
|
||||||
max_length=5,
|
max_length=5,
|
||||||
required=False,
|
required=False,
|
||||||
label=_("Duration (hh:mm)"))
|
label=ugettext_lazy("Duration (hh:mm)"))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Item
|
model = Item
|
||||||
@ -46,7 +46,7 @@ def gen_weight_choices():
|
|||||||
return zip(*(range(-50, 51), range(-50, 51)))
|
return zip(*(range(-50, 51), range(-50, 51)))
|
||||||
|
|
||||||
|
|
||||||
class ItemOrderForm(forms.Form, CssClassMixin):
|
class ItemOrderForm(CssClassMixin, forms.Form):
|
||||||
"""
|
"""
|
||||||
Form to change the order of the items.
|
Form to change the order of the items.
|
||||||
"""
|
"""
|
||||||
@ -57,10 +57,3 @@ class ItemOrderForm(forms.Form, CssClassMixin):
|
|||||||
widget=forms.HiddenInput(attrs={'class': 'menu-mlid'}))
|
widget=forms.HiddenInput(attrs={'class': 'menu-mlid'}))
|
||||||
parent = forms.IntegerField(
|
parent = forms.IntegerField(
|
||||||
widget=forms.HiddenInput(attrs={'class': 'menu-plid'}))
|
widget=forms.HiddenInput(attrs={'class': 'menu-plid'}))
|
||||||
|
|
||||||
|
|
||||||
class ConfigForm(CssClassMixin, forms.Form):
|
|
||||||
agenda_start_event_date_time = forms.CharField(
|
|
||||||
widget=forms.DateTimeInput(format='%d.%m.%Y %H:%M'),
|
|
||||||
required=False,
|
|
||||||
label=_("Begin of event"))
|
|
||||||
|
@ -16,7 +16,7 @@ from django.utils.translation import ugettext_lazy as _, ugettext_noop, ugettext
|
|||||||
|
|
||||||
from mptt.models import MPTTModel, TreeForeignKey
|
from mptt.models import MPTTModel, TreeForeignKey
|
||||||
|
|
||||||
from openslides.config.models import config
|
from openslides.config.api import config
|
||||||
from openslides.projector.projector import SlideMixin
|
from openslides.projector.projector import SlideMixin
|
||||||
from openslides.projector.api import (
|
from openslides.projector.api import (
|
||||||
register_slidemodel, get_slide_from_sid, register_slidefunc)
|
register_slidemodel, get_slide_from_sid, register_slidefunc)
|
||||||
|
@ -6,17 +6,44 @@
|
|||||||
|
|
||||||
Signals for the agenda app.
|
Signals for the agenda app.
|
||||||
|
|
||||||
:copyright: (c) 2011-2013 by the OpenSlides team, see AUTHORS.
|
:copyright: (c) 2011–2013 by the OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
from django import forms
|
||||||
|
from django.utils.translation import ugettext_lazy, ugettext_noop, ugettext as _
|
||||||
|
|
||||||
from openslides.config.signals import default_config_value
|
from openslides.config.signals import config_signal
|
||||||
|
from openslides.config.api import ConfigVariable, ConfigPage
|
||||||
|
|
||||||
|
|
||||||
@receiver(default_config_value, dispatch_uid="agenda_default_config")
|
# TODO: Reinsert the datepicker scripts in the template
|
||||||
def default_config(sender, key, **kwargs):
|
|
||||||
"""Return the default config values for the agenda app."""
|
@receiver(config_signal, dispatch_uid='setup_agenda_config_page')
|
||||||
return {
|
def setup_agenda_config_page(sender, **kwargs):
|
||||||
'agenda_start_event_date_time': ''}.get(key)
|
"""
|
||||||
|
Agenda config variables.
|
||||||
|
"""
|
||||||
|
# TODO: Insert validator for the format or use other field carefully.
|
||||||
|
agenda_start_event_date_time = ConfigVariable(
|
||||||
|
name='agenda_start_event_date_time',
|
||||||
|
default_value='',
|
||||||
|
form_field=forms.CharField(
|
||||||
|
widget=forms.DateTimeInput(format='%d.%m.%Y %H:%M'),
|
||||||
|
required=False,
|
||||||
|
label=ugettext_lazy('Begin of event'),
|
||||||
|
help_text=_('Input format: DD.MM.YYYY HH:MM')))
|
||||||
|
|
||||||
|
extra_stylefiles = ['styles/timepicker.css', 'styles/jquery-ui/jquery-ui.custom.min.css']
|
||||||
|
extra_javascript = ['javascript/jquery-ui.custom.min.js',
|
||||||
|
'javascript/jquery-ui-timepicker-addon.min.js',
|
||||||
|
'javascript/jquery-ui-sliderAccess.min.js']
|
||||||
|
|
||||||
|
return ConfigPage(title=ugettext_noop('Agenda'),
|
||||||
|
url='agenda',
|
||||||
|
required_permission='config.can_manage',
|
||||||
|
weight=20,
|
||||||
|
variables=(agenda_start_event_date_time,),
|
||||||
|
extra_context={'extra_stylefiles': extra_stylefiles,
|
||||||
|
'extra_javascript': extra_javascript})
|
||||||
|
@ -20,8 +20,7 @@ from django.db.models import Model
|
|||||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||||
from django.views.generic.detail import SingleObjectMixin
|
from django.views.generic.detail import SingleObjectMixin
|
||||||
|
|
||||||
from openslides.config.models import config
|
from openslides.config.api import config
|
||||||
from openslides.agenda.forms import ConfigForm
|
|
||||||
from openslides.utils.pdf import stylesheet
|
from openslides.utils.pdf import stylesheet
|
||||||
from openslides.utils.views import (
|
from openslides.utils.views import (
|
||||||
TemplateView, RedirectView, UpdateView, CreateView, DeleteView, PDFView,
|
TemplateView, RedirectView, UpdateView, CreateView, DeleteView, PDFView,
|
||||||
@ -225,29 +224,9 @@ class AgendaPDF(PDFView):
|
|||||||
story.append(Paragraph(item.get_title(), stylesheet['Item']))
|
story.append(Paragraph(item.get_title(), stylesheet['Item']))
|
||||||
|
|
||||||
|
|
||||||
class Config(FormView):
|
|
||||||
"""
|
|
||||||
Config page for the agenda app.
|
|
||||||
"""
|
|
||||||
permission_required = 'config.can_manage_config'
|
|
||||||
form_class = ConfigForm
|
|
||||||
template_name = 'agenda/config.html'
|
|
||||||
success_url_name = 'config_agenda'
|
|
||||||
|
|
||||||
def get_initial(self):
|
|
||||||
return {
|
|
||||||
'agenda_start_event_date_time': config['agenda_start_event_date_time'],
|
|
||||||
}
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
config['agenda_start_event_date_time'] = form.cleaned_data['agenda_start_event_date_time']
|
|
||||||
messages.success(self.request, _('Agenda settings successfully saved.'))
|
|
||||||
return super(Config, self).form_valid(form)
|
|
||||||
|
|
||||||
|
|
||||||
def register_tab(request):
|
def register_tab(request):
|
||||||
"""
|
"""
|
||||||
register the agenda tab.
|
Registers the agenda tab.
|
||||||
"""
|
"""
|
||||||
selected = request.path.startswith('/agenda/')
|
selected = request.path.startswith('/agenda/')
|
||||||
return Tab(
|
return Tab(
|
||||||
@ -261,7 +240,7 @@ def register_tab(request):
|
|||||||
|
|
||||||
def get_widgets(request):
|
def get_widgets(request):
|
||||||
"""
|
"""
|
||||||
return the agenda widget for the projector-tab.
|
Returns the agenda widget for the projector tab.
|
||||||
"""
|
"""
|
||||||
return [Widget(
|
return [Widget(
|
||||||
name='agenda',
|
name='agenda',
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
openslides.assignment
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The OpenSlides assignment app.
|
||||||
|
|
||||||
|
:copyright: (c) 2011–2013 by the OpenSlides team, see AUTHORS.
|
||||||
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from . import signals
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
Forms for the assignment app.
|
Forms for the assignment app.
|
||||||
|
|
||||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -33,39 +33,3 @@ class AssignmentRunForm(forms.Form, CssClassMixin):
|
|||||||
widget=forms.Select(attrs={'class': 'medium-input'}),
|
widget=forms.Select(attrs={'class': 'medium-input'}),
|
||||||
label=_("Nominate a participant"),
|
label=_("Nominate a participant"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ConfigForm(forms.Form, CssClassMixin):
|
|
||||||
assignment_publish_winner_results_only = forms.BooleanField(
|
|
||||||
required=False,
|
|
||||||
label=_("Only publish voting results for selected winners "
|
|
||||||
"(Projector view only)"))
|
|
||||||
assignment_pdf_ballot_papers_selection = forms.ChoiceField(
|
|
||||||
widget=forms.Select(),
|
|
||||||
required=False,
|
|
||||||
label=_("Number of ballot papers (selection)"),
|
|
||||||
choices=(
|
|
||||||
("NUMBER_OF_DELEGATES", _("Number of all delegates")),
|
|
||||||
("NUMBER_OF_ALL_PARTICIPANTS", _("Number of all participants")),
|
|
||||||
("CUSTOM_NUMBER", _("Use the following custom number"))))
|
|
||||||
assignment_pdf_ballot_papers_number = forms.IntegerField(
|
|
||||||
widget=forms.TextInput(attrs={'class': 'small-input'}),
|
|
||||||
required=False,
|
|
||||||
min_value=1,
|
|
||||||
label=_("Custom number of ballot papers"))
|
|
||||||
assignment_pdf_title = forms.CharField(
|
|
||||||
widget=forms.TextInput(),
|
|
||||||
required=False,
|
|
||||||
label=_("Title for PDF document (all elections)"))
|
|
||||||
assignment_pdf_preamble = forms.CharField(
|
|
||||||
widget=forms.Textarea(),
|
|
||||||
required=False,
|
|
||||||
label=_("Preamble text for PDF document (all elections)"))
|
|
||||||
assignment_poll_vote_values = forms.ChoiceField(
|
|
||||||
widget=forms.Select(),
|
|
||||||
required=False,
|
|
||||||
label=_("Election method"),
|
|
||||||
choices=(
|
|
||||||
("auto", _("Automatic assign of method.")),
|
|
||||||
("votes", _("Always one option per candidate.")),
|
|
||||||
("yesnoabstain", _("Always Yes-No-Abstain per candidate."))))
|
|
||||||
|
@ -6,18 +6,16 @@
|
|||||||
|
|
||||||
Models for the assignment app.
|
Models for the assignment app.
|
||||||
|
|
||||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.dispatch import receiver
|
from django.utils.translation import ugettext_lazy as _, ugettext_noop # TODO Change this in the code
|
||||||
from django.utils.translation import ugettext_lazy as _, ugettext_noop
|
|
||||||
|
|
||||||
from openslides.utils.person import PersonField
|
from openslides.utils.person import PersonField
|
||||||
from openslides.config.models import config
|
from openslides.config.api import config
|
||||||
from openslides.config.signals import default_config_value
|
|
||||||
from openslides.projector.api import register_slidemodel
|
from openslides.projector.api import register_slidemodel
|
||||||
from openslides.projector.projector import SlideMixin
|
from openslides.projector.projector import SlideMixin
|
||||||
from openslides.poll.models import (
|
from openslides.poll.models import (
|
||||||
@ -308,15 +306,3 @@ class AssignmentPoll(BasePoll, CountInvalid, CountVotesCast, PublishPollMixin):
|
|||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return _("Ballot %d") % self.get_ballot()
|
return _("Ballot %d") % self.get_ballot()
|
||||||
|
|
||||||
|
|
||||||
@receiver(default_config_value, dispatch_uid="assignment_default_config")
|
|
||||||
def default_config(sender, key, **kwargs):
|
|
||||||
return {
|
|
||||||
'assignment_publish_winner_results_only': False,
|
|
||||||
'assignment_pdf_ballot_papers_selection': 'CUSTOM_NUMBER',
|
|
||||||
'assignment_pdf_ballot_papers_number': '8',
|
|
||||||
'assignment_pdf_title': _('Elections'),
|
|
||||||
'assignment_pdf_preamble': '',
|
|
||||||
'assignment_poll_vote_values': 'auto',
|
|
||||||
}.get(key)
|
|
||||||
|
87
openslides/assignment/signals.py
Normal file
87
openslides/assignment/signals.py
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
openslides.assignment.signals
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Signals for the assignment app.
|
||||||
|
|
||||||
|
:copyright: (c) 2011–2013 by the OpenSlides team, see AUTHORS.
|
||||||
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.dispatch import receiver
|
||||||
|
from django import forms
|
||||||
|
from django.utils.translation import ugettext_lazy, ugettext_noop, ugettext as _
|
||||||
|
|
||||||
|
from openslides.config.signals import config_signal
|
||||||
|
from openslides.config.api import ConfigVariable, ConfigPage
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(config_signal, dispatch_uid='setup_assignment_config_page')
|
||||||
|
def setup_assignment_config_page(sender, **kwargs):
|
||||||
|
"""
|
||||||
|
Assignment config variables.
|
||||||
|
"""
|
||||||
|
assignment_publish_winner_results_only = ConfigVariable(
|
||||||
|
name='assignment_publish_winner_results_only',
|
||||||
|
default_value=False,
|
||||||
|
form_field=forms.BooleanField(
|
||||||
|
required=False,
|
||||||
|
label=_('Only publish voting results for selected winners '
|
||||||
|
'(Projector view only)')))
|
||||||
|
assignment_pdf_ballot_papers_selection = ConfigVariable(
|
||||||
|
name='assignment_pdf_ballot_papers_selection',
|
||||||
|
default_value='CUSTOM_NUMBER',
|
||||||
|
form_field=forms.ChoiceField(
|
||||||
|
widget=forms.Select(),
|
||||||
|
required=False,
|
||||||
|
label=_('Number of ballot papers (selection)'),
|
||||||
|
choices=(
|
||||||
|
('NUMBER_OF_DELEGATES', _('Number of all delegates')),
|
||||||
|
('NUMBER_OF_ALL_PARTICIPANTS', _('Number of all participants')),
|
||||||
|
('CUSTOM_NUMBER', _('Use the following custom number')))))
|
||||||
|
assignment_pdf_ballot_papers_number = ConfigVariable(
|
||||||
|
name='assignment_pdf_ballot_papers_number',
|
||||||
|
default_value=8,
|
||||||
|
form_field=forms.IntegerField(
|
||||||
|
widget=forms.TextInput(attrs={'class': 'small-input'}),
|
||||||
|
required=False,
|
||||||
|
min_value=1,
|
||||||
|
label=_('Custom number of ballot papers')))
|
||||||
|
assignment_pdf_title = ConfigVariable(
|
||||||
|
name='assignment_pdf_title',
|
||||||
|
default_value=_('Elections'),
|
||||||
|
form_field=forms.CharField(
|
||||||
|
widget=forms.TextInput(),
|
||||||
|
required=False,
|
||||||
|
label=_('Title for PDF document (all elections)')))
|
||||||
|
assignment_pdf_preamble = ConfigVariable(
|
||||||
|
name='assignment_pdf_preamble',
|
||||||
|
default_value='',
|
||||||
|
form_field=forms.CharField(
|
||||||
|
widget=forms.Textarea(),
|
||||||
|
required=False,
|
||||||
|
label=_('Preamble text for PDF document (all elections)')))
|
||||||
|
assignment_poll_vote_values = ConfigVariable(
|
||||||
|
name='assignment_poll_vote_values',
|
||||||
|
default_value='auto',
|
||||||
|
form_field=forms.ChoiceField(
|
||||||
|
widget=forms.Select(),
|
||||||
|
required=False,
|
||||||
|
label=_('Election method'),
|
||||||
|
choices=(
|
||||||
|
('auto', _('Automatic assign of method.')),
|
||||||
|
('votes', _('Always one option per candidate.')),
|
||||||
|
('yesnoabstain', _('Always Yes-No-Abstain per candidate.')))))
|
||||||
|
|
||||||
|
return ConfigPage(title=ugettext_noop('Elections'),
|
||||||
|
url='assignment',
|
||||||
|
required_permission='config.can_manage',
|
||||||
|
weight=40,
|
||||||
|
variables=(assignment_publish_winner_results_only,
|
||||||
|
assignment_pdf_ballot_papers_selection,
|
||||||
|
assignment_pdf_ballot_papers_number,
|
||||||
|
assignment_pdf_title,
|
||||||
|
assignment_pdf_preamble,
|
||||||
|
assignment_poll_vote_values))
|
@ -1,23 +0,0 @@
|
|||||||
{% extends "config/base_config.html" %}
|
|
||||||
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block title %}{{ block.super }} – {% trans "Election settings" %}{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<h1>
|
|
||||||
{% trans "Configuration" %}
|
|
||||||
<small>{% trans "Elections" %}</small>
|
|
||||||
{% block config_submenu %}{{ block.super }}{% endblock %}
|
|
||||||
</h1>
|
|
||||||
<form action="" method="post">{% csrf_token %}
|
|
||||||
{% include "form.html" %}
|
|
||||||
<p>
|
|
||||||
{% include "formbuttons_save.html" %}
|
|
||||||
<a href="{% url 'config_assignment' %}" class="btn">
|
|
||||||
{% trans 'Cancel' %}
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
<small>* {% trans "required" %}</small>
|
|
||||||
</form>
|
|
||||||
{% endblock %}
|
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
URL list for the assignment app.
|
URL list for the assignment app.
|
||||||
|
|
||||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
Views for the assignment app.
|
Views for the assignment app.
|
||||||
|
|
||||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -30,14 +30,13 @@ from openslides.utils.utils import (
|
|||||||
template, permission_required, gen_confirm_form, del_confirm_form, ajax_request)
|
template, permission_required, gen_confirm_form, del_confirm_form, ajax_request)
|
||||||
from openslides.utils.views import FormView, DeleteView, PDFView, RedirectView
|
from openslides.utils.views import FormView, DeleteView, PDFView, RedirectView
|
||||||
from openslides.utils.person import get_person
|
from openslides.utils.person import get_person
|
||||||
from openslides.config.models import config
|
from openslides.config.api import config
|
||||||
from openslides.participant.models import User
|
from openslides.participant.models import User
|
||||||
from openslides.projector.projector import Widget
|
from openslides.projector.projector import Widget
|
||||||
from openslides.poll.views import PollFormView
|
from openslides.poll.views import PollFormView
|
||||||
from openslides.agenda.models import Item
|
from openslides.agenda.models import Item
|
||||||
from openslides.assignment.models import Assignment, AssignmentPoll
|
from openslides.assignment.models import Assignment, AssignmentPoll
|
||||||
from openslides.assignment.forms import (
|
from openslides.assignment.forms import AssignmentForm, AssignmentRunForm
|
||||||
AssignmentForm, AssignmentRunForm, ConfigForm)
|
|
||||||
|
|
||||||
|
|
||||||
@permission_required('assignment.can_see_assignment')
|
@permission_required('assignment.can_see_assignment')
|
||||||
@ -633,45 +632,6 @@ class AssignmentPollPDF(PDFView):
|
|||||||
story.append(t)
|
story.append(t)
|
||||||
|
|
||||||
|
|
||||||
class Config(FormView):
|
|
||||||
permission_required = 'config.can_manage_config'
|
|
||||||
form_class = ConfigForm
|
|
||||||
template_name = 'assignment/config.html'
|
|
||||||
success_url_name = 'config_assignment'
|
|
||||||
|
|
||||||
def get_initial(self):
|
|
||||||
return {
|
|
||||||
'assignment_publish_winner_results_only':
|
|
||||||
config['assignment_publish_winner_results_only'],
|
|
||||||
'assignment_pdf_ballot_papers_selection':
|
|
||||||
config['assignment_pdf_ballot_papers_selection'],
|
|
||||||
'assignment_pdf_ballot_papers_number':
|
|
||||||
config['assignment_pdf_ballot_papers_number'],
|
|
||||||
'assignment_pdf_title': config['assignment_pdf_title'],
|
|
||||||
'assignment_pdf_preamble': config['assignment_pdf_preamble'],
|
|
||||||
'assignment_poll_vote_values':
|
|
||||||
config['assignment_poll_vote_values']}
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
if form.cleaned_data['assignment_publish_winner_results_only']:
|
|
||||||
config['assignment_publish_winner_results_only'] = True
|
|
||||||
else:
|
|
||||||
config['assignment_publish_winner_results_only'] = False
|
|
||||||
config['assignment_pdf_ballot_papers_selection'] = \
|
|
||||||
form.cleaned_data['assignment_pdf_ballot_papers_selection']
|
|
||||||
config['assignment_pdf_ballot_papers_number'] = \
|
|
||||||
form.cleaned_data['assignment_pdf_ballot_papers_number']
|
|
||||||
config['assignment_pdf_title'] = \
|
|
||||||
form.cleaned_data['assignment_pdf_title']
|
|
||||||
config['assignment_pdf_preamble'] = \
|
|
||||||
form.cleaned_data['assignment_pdf_preamble']
|
|
||||||
config['assignment_poll_vote_values'] = \
|
|
||||||
form.cleaned_data['assignment_poll_vote_values']
|
|
||||||
messages.success(
|
|
||||||
self.request, _('Election settings successfully saved.'))
|
|
||||||
return super(Config, self).form_valid(form)
|
|
||||||
|
|
||||||
|
|
||||||
def register_tab(request):
|
def register_tab(request):
|
||||||
selected = request.path.startswith('/assignment/')
|
selected = request.path.startswith('/assignment/')
|
||||||
return Tab(
|
return Tab(
|
||||||
|
147
openslides/config/api.py
Normal file
147
openslides/config/api.py
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
openslides.config.api
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Api for the config app.
|
||||||
|
|
||||||
|
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||||
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .models import ConfigStore
|
||||||
|
from .exceptions import ConfigError, ConfigNotFound
|
||||||
|
from .signals import config_signal
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigHandler(object):
|
||||||
|
"""
|
||||||
|
An simple object class to wrap the config variables. It is a container
|
||||||
|
object. To get a config variable use x = config[...], to set it use
|
||||||
|
config[...] = x.
|
||||||
|
"""
|
||||||
|
def __getitem__(self, key):
|
||||||
|
try:
|
||||||
|
return self._cache[key]
|
||||||
|
except KeyError:
|
||||||
|
raise ConfigNotFound('The config variable %s was not found.' % key)
|
||||||
|
except AttributeError:
|
||||||
|
self.setup_cache()
|
||||||
|
return self[key]
|
||||||
|
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
updated_rows = ConfigStore.objects.filter(key=key).update(value=value)
|
||||||
|
if not updated_rows:
|
||||||
|
ConfigStore.objects.create(key=key, value=value)
|
||||||
|
self._cache[key] = value
|
||||||
|
|
||||||
|
def setup_cache(self):
|
||||||
|
"""
|
||||||
|
Loads all config variables from the database and by sending a
|
||||||
|
signal to get the default into the cache.
|
||||||
|
"""
|
||||||
|
self._cache = {}
|
||||||
|
for receiver, config_page in config_signal.send(sender=self):
|
||||||
|
for config_variable in config_page.variables:
|
||||||
|
if config_variable.name in self._cache:
|
||||||
|
raise ConfigError('Too many values for config variable %s found.' % config_variable.name)
|
||||||
|
self._cache[config_variable.name] = config_variable.default_value
|
||||||
|
for config_object in ConfigStore.objects.all():
|
||||||
|
self._cache[config_object.key] = config_object.value
|
||||||
|
|
||||||
|
def __contains__(self, key):
|
||||||
|
try:
|
||||||
|
config[key]
|
||||||
|
except ConfigNotFound:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
config = ConfigHandler()
|
||||||
|
"""
|
||||||
|
Final entry point to get an set config variables. To get a config variable
|
||||||
|
use x = config[...], to set it use config[...] = x.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigBasePage(object):
|
||||||
|
"""
|
||||||
|
An abstract base class for simple and grouped config pages. The
|
||||||
|
attributes title and url are required. The attribute weight is used
|
||||||
|
for the order of the links in the submenu of the views. The attribute
|
||||||
|
extra_context can be used to insert extra css and js files into the
|
||||||
|
template.
|
||||||
|
"""
|
||||||
|
def __init__(self, title, url, required_permission=None, weight=0, extra_context={}):
|
||||||
|
self.title = title
|
||||||
|
self.url = url
|
||||||
|
self.required_permission = required_permission
|
||||||
|
self.weight = weight
|
||||||
|
self.extra_context = extra_context
|
||||||
|
|
||||||
|
def is_shown(self):
|
||||||
|
"""
|
||||||
|
Returns True if at least one variable of the page has a form field.
|
||||||
|
"""
|
||||||
|
for variable in self.variables:
|
||||||
|
if variable.form_field is not None:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigGroupedPage(ConfigBasePage):
|
||||||
|
"""
|
||||||
|
A simple object class for a grouped config page. Developers have to
|
||||||
|
set the groups attribute (tuple). The config variables are available
|
||||||
|
via the variables attribute. The page is shown as view in the config
|
||||||
|
tab, if there is at least one variable with a form field.
|
||||||
|
"""
|
||||||
|
def __init__(self, groups, **kwargs):
|
||||||
|
self.groups = groups
|
||||||
|
super(ConfigGroupedPage, self).__init__(**kwargs)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def variables(self):
|
||||||
|
for group in self.groups:
|
||||||
|
for variable in group.variables:
|
||||||
|
yield variable
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigPage(ConfigBasePage):
|
||||||
|
"""
|
||||||
|
A simple object class for a ungrouped config page. Developers have
|
||||||
|
to set the variables (tuple) directly. The page is shown as view in
|
||||||
|
the config tab, if there is at least one variable with a form field.
|
||||||
|
"""
|
||||||
|
def __init__(self, variables, **kwargs):
|
||||||
|
self.variables = variables
|
||||||
|
super(ConfigPage, self).__init__(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigGroup(object):
|
||||||
|
"""
|
||||||
|
A simple object class representing a group of variables (tuple) with
|
||||||
|
a special title.
|
||||||
|
"""
|
||||||
|
def __init__(self, title, variables):
|
||||||
|
self.title = title
|
||||||
|
self.variables = variables
|
||||||
|
|
||||||
|
def get_field_names(self):
|
||||||
|
return [variable.name for variable in self.variables if variable.form_field is not None]
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigVariable(object):
|
||||||
|
"""
|
||||||
|
A simple object class to wrap new config variables. The keyword
|
||||||
|
arguments 'name' and 'default_value' are required. The keyword
|
||||||
|
argument 'form_field' has to be set, if the variable should appear
|
||||||
|
on the ConfigView.
|
||||||
|
"""
|
||||||
|
def __init__(self, name, default_value, form_field=None):
|
||||||
|
self.name = name
|
||||||
|
self.default_value = default_value
|
||||||
|
self.form_field = form_field
|
21
openslides/config/exceptions.py
Normal file
21
openslides/config/exceptions.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
openslides.config.exceptions
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Exceptions for the config app.
|
||||||
|
|
||||||
|
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||||
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from openslides.utils.exceptions import OpenSlidesError
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigError(OpenSlidesError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigNotFound(ConfigError):
|
||||||
|
pass
|
@ -1,67 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
openslides.config.forms
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Forms for the config app.
|
|
||||||
|
|
||||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from django import forms
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
|
||||||
|
|
||||||
from openslides.utils.forms import CssClassMixin
|
|
||||||
|
|
||||||
|
|
||||||
class GeneralConfigForm(forms.Form, CssClassMixin):
|
|
||||||
event_name = forms.CharField(
|
|
||||||
widget=forms.TextInput(),
|
|
||||||
label=_("Event name"),
|
|
||||||
max_length=30,
|
|
||||||
)
|
|
||||||
|
|
||||||
event_description = forms.CharField(
|
|
||||||
widget=forms.TextInput(),
|
|
||||||
label=_("Short description of event"),
|
|
||||||
required=False,
|
|
||||||
max_length=100,
|
|
||||||
|
|
||||||
)
|
|
||||||
|
|
||||||
event_date = forms.CharField(
|
|
||||||
widget=forms.TextInput(),
|
|
||||||
label=_("Event date"),
|
|
||||||
required=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
event_location = forms.CharField(
|
|
||||||
widget=forms.TextInput(),
|
|
||||||
label=_("Event location"),
|
|
||||||
required=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
event_organizer = forms.CharField(
|
|
||||||
widget=forms.TextInput(),
|
|
||||||
label=_("Event organizer"),
|
|
||||||
required=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
system_enable_anonymous = forms.BooleanField(
|
|
||||||
label=_("Allow access for anonymous guest users"),
|
|
||||||
required=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
welcome_title = forms.CharField(
|
|
||||||
widget=forms.TextInput(),
|
|
||||||
label=_("Title"),
|
|
||||||
required=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
welcome_text = forms.CharField(
|
|
||||||
widget=forms.Textarea(),
|
|
||||||
label=_("Welcome text"),
|
|
||||||
required=False,
|
|
||||||
)
|
|
21
openslides/config/middleware.py
Normal file
21
openslides/config/middleware.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
openslides.config.middleware
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Middleware for the config app.
|
||||||
|
|
||||||
|
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||||
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from openslides.config.api import config
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigCacheMiddleware(object):
|
||||||
|
"""
|
||||||
|
Middleware to refresh the config cache before processing any view.
|
||||||
|
"""
|
||||||
|
def process_request(self, request):
|
||||||
|
config.setup_cache()
|
@ -6,125 +6,26 @@
|
|||||||
|
|
||||||
Models for the config app.
|
Models for the config app.
|
||||||
|
|
||||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.dispatch import receiver
|
from django.utils.translation import ugettext_noop
|
||||||
from django.utils.importlib import import_module
|
|
||||||
from django.utils.translation import ugettext_lazy as _, ugettext_noop
|
|
||||||
|
|
||||||
from openslides.utils.jsonfield import JSONField
|
from openslides.utils.jsonfield import JSONField
|
||||||
from openslides.utils.signals import template_manipulation
|
|
||||||
|
|
||||||
from openslides.config.signals import default_config_value
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigStore(models.Model):
|
class ConfigStore(models.Model):
|
||||||
"""
|
"""
|
||||||
Stores the config values.
|
A model class to store all config variables in the database.
|
||||||
"""
|
"""
|
||||||
key = models.CharField(max_length=100, primary_key=True)
|
|
||||||
value = JSONField()
|
|
||||||
|
|
||||||
def __unicode__(self):
|
key = models.CharField(max_length=255, primary_key=True)
|
||||||
return self.key
|
"""A string, the key of the config variable."""
|
||||||
|
|
||||||
|
value = JSONField()
|
||||||
|
"""The value of the config variable. """
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = 'config'
|
permissions = (('can_manage', ugettext_noop('Can manage configuration')),)
|
||||||
permissions = (
|
|
||||||
('can_manage_config', ugettext_noop("Can manage configuration")),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Config(object):
|
|
||||||
"""
|
|
||||||
Access the config values via config[...]
|
|
||||||
"""
|
|
||||||
def __getitem__(self, key):
|
|
||||||
try:
|
|
||||||
return ConfigStore.objects.get(key=key).value
|
|
||||||
except ConfigStore.DoesNotExist:
|
|
||||||
pass
|
|
||||||
|
|
||||||
for receiver, value in default_config_value.send(sender='config',
|
|
||||||
key=key):
|
|
||||||
if value is not None:
|
|
||||||
return value
|
|
||||||
if settings.DEBUG:
|
|
||||||
print "No default value for: %s" % key
|
|
||||||
return None
|
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
|
||||||
try:
|
|
||||||
c = ConfigStore.objects.get(pk=key)
|
|
||||||
except ConfigStore.DoesNotExist:
|
|
||||||
c = ConfigStore(pk=key)
|
|
||||||
c.value = value
|
|
||||||
c.save()
|
|
||||||
|
|
||||||
def __contains__(self, item):
|
|
||||||
return ConfigStore.objects.filter(key=item).exists()
|
|
||||||
|
|
||||||
config = Config()
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(default_config_value, dispatch_uid="config_default_config")
|
|
||||||
def default_config(sender, key, **kwargs):
|
|
||||||
"""
|
|
||||||
Global default values.
|
|
||||||
"""
|
|
||||||
return {
|
|
||||||
'event_name': 'OpenSlides',
|
|
||||||
'event_description':
|
|
||||||
_('Presentation and assembly system'),
|
|
||||||
'event_date': '',
|
|
||||||
'event_location': '',
|
|
||||||
'event_organizer': '',
|
|
||||||
'presentation': '',
|
|
||||||
'welcome_title': _('Welcome to OpenSlides'),
|
|
||||||
'welcome_text': _('[Place for your welcome text.]'),
|
|
||||||
'system_enable_anonymous': False,
|
|
||||||
}.get(key)
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(template_manipulation, dispatch_uid="config_submenu")
|
|
||||||
def set_submenu(sender, request, context, **kwargs):
|
|
||||||
"""
|
|
||||||
Submenu for the config tab.
|
|
||||||
"""
|
|
||||||
if not request.path.startswith('/config/'):
|
|
||||||
return None
|
|
||||||
menu_links = [
|
|
||||||
(reverse('config_general'), _('General'),
|
|
||||||
request.path == reverse('config_general')),
|
|
||||||
]
|
|
||||||
|
|
||||||
for app in settings.INSTALLED_APPS:
|
|
||||||
try:
|
|
||||||
mod = import_module(app)
|
|
||||||
views = mod.views
|
|
||||||
views.Config
|
|
||||||
except (ImportError, AttributeError):
|
|
||||||
continue
|
|
||||||
|
|
||||||
appname = mod.__name__.split('.')[-1]
|
|
||||||
|
|
||||||
selected = reverse('config_%s' % appname) == request.path
|
|
||||||
try:
|
|
||||||
title = mod.NAME
|
|
||||||
except AttributeError:
|
|
||||||
title = appname.title()
|
|
||||||
menu_links.append(
|
|
||||||
(reverse('config_%s' % appname), _(title), selected)
|
|
||||||
)
|
|
||||||
|
|
||||||
menu_links.append((
|
|
||||||
reverse('config_version'), _('Version'),
|
|
||||||
request.path == reverse('config_version')))
|
|
||||||
|
|
||||||
context.update({
|
|
||||||
'menu_links': menu_links})
|
|
||||||
|
@ -4,12 +4,14 @@
|
|||||||
openslides.config.signals
|
openslides.config.signals
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Defines Signals for the config.
|
Signals for the config app.
|
||||||
|
|
||||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.dispatch import Signal
|
from django.dispatch import Signal
|
||||||
|
|
||||||
default_config_value = Signal(providing_args=['key'])
|
|
||||||
|
config_signal = Signal(providing_args=[])
|
||||||
|
"""Signal to get all config tabs from all apps."""
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block config_submenu %}
|
|
||||||
<small class="pull-right">
|
|
||||||
<div class="btn-toolbar">
|
|
||||||
<div class="btn-group">
|
|
||||||
{% for menu_link in menu_links %}
|
|
||||||
<a href="{{ menu_link.0 }}" class="btn btn-mini {% if menu_link.2 %}active{% endif %}">{{ menu_link.1 }}</a>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</small>
|
|
||||||
{% endblock %}
|
|
@ -1,19 +1,12 @@
|
|||||||
{% extends "config/base_config.html" %}
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% get_current_language as LANGUAGE_CODE %}
|
|
||||||
|
|
||||||
|
{% get_current_language as LANGUAGE_CODE %}
|
||||||
{% load staticfiles %}
|
{% load staticfiles %}
|
||||||
|
|
||||||
{% block header %}
|
|
||||||
<link type="text/css" rel="stylesheet" media="all" href="{% static 'styles/jquery-ui/jquery-ui.custom.min.css' %}" />
|
|
||||||
<link type="text/css" rel="stylesheet" media="all" href="{% static 'styles/timepicker.css' %}" />
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block javascript %}
|
{% block javascript %}
|
||||||
<script type="text/javascript" src="{% static 'javascript/jquery-ui.custom.min.js' %}"></script>
|
{{ block.super }}
|
||||||
<script type="text/javascript" src="{% static 'javascript/jquery-ui-timepicker-addon.min.js' %}"></script>
|
|
||||||
<script type="text/javascript" src="{% static 'javascript/jquery-ui-sliderAccess.min.js' %}"></script>
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(function() {
|
$(function() {
|
||||||
$.datepicker.regional['{{ LANGUAGE_CODE }}'] = {
|
$.datepicker.regional['{{ LANGUAGE_CODE }}'] = {
|
||||||
@ -63,23 +56,47 @@
|
|||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block title %}{{ block.super }} – {% trans "Agenda settings" %}{% endblock %}
|
|
||||||
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>
|
<h1>
|
||||||
{% trans "Configuration" %}
|
{% trans 'Configuration' %}
|
||||||
<small>{% trans "Agenda" %}</small>
|
<small>{% trans active_config_page.title %}</small>
|
||||||
{% block config_submenu %}{{ block.super }}{% endblock %}
|
<small class="pull-right">
|
||||||
</h1>
|
<div class="btn-toolbar">
|
||||||
<form action="" method="post">{% csrf_token %}
|
<div class="btn-group">
|
||||||
{% include "form.html" %}
|
{% for config_page_dict in config_pages_list %}
|
||||||
<p>
|
<a href="/config/{{ config_page_dict.config_page.url }}/" class="btn btn-mini {% if config_page_dict.active %}active{% endif %}">{{ config_page_dict.config_page.title }}</a>
|
||||||
{% include "formbuttons_save.html" %}
|
{% endfor %}
|
||||||
<a href="{% url 'config_agenda' %}" class="btn">
|
<a href="{% url 'core_version' %}" class="btn btn-mini">{% trans 'Version' %}</a>
|
||||||
{% trans 'Cancel' %}
|
</div>
|
||||||
</a>
|
</div>
|
||||||
</p>
|
</small>
|
||||||
<small>* {% trans "required" %}</small>
|
</h1>
|
||||||
</form>
|
<form action="" method="post">{% csrf_token %}
|
||||||
|
{% for group in groups %}
|
||||||
|
<fieldset>
|
||||||
|
<legend>{{ group.title }}</legend>
|
||||||
|
{% for field in form %}
|
||||||
|
{% if field.name in group.get_field_names %}
|
||||||
|
<div class="control-group {% if field.errors %}error{% endif%}">
|
||||||
|
<label for="id_{{ field.name }}">{{ field.label }}{% if field.field.required %}<span class="required">*</span>{% endif %}:</label>
|
||||||
|
{{ field }}
|
||||||
|
{% if field.errors %}
|
||||||
|
<span class="help-inline">{{ field.errors }}</span>
|
||||||
|
{% endif %}
|
||||||
|
{% if field.help_text %}
|
||||||
|
<span class="help-inline">{{ field.help_text }}</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</fieldset>
|
||||||
|
{% empty %}
|
||||||
|
{% include 'form.html' %}
|
||||||
|
{% endfor %}
|
||||||
|
<p>
|
||||||
|
{% include 'formbuttons_save.html' %}
|
||||||
|
<a href="/config/{{ active_config_page.url }}/" class="btn">{% trans 'Cancel' %}</a>
|
||||||
|
</p>
|
||||||
|
<small>* {% trans 'required' %}</small>
|
||||||
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -1,75 +0,0 @@
|
|||||||
{% extends "config/base_config.html" %}
|
|
||||||
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block title %}{{ block.super }} – {% trans "General settings" %}{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<h1>
|
|
||||||
{% trans "Configuration" %}
|
|
||||||
<small>{% trans "General" %}</small>
|
|
||||||
{% block config_submenu %}{{ block.super }}{% endblock %}
|
|
||||||
</h1>
|
|
||||||
<form action="" method="post">{% csrf_token %}
|
|
||||||
<fieldset>
|
|
||||||
<legend>{% trans "Event" %}</legend>
|
|
||||||
{% for field in form %}
|
|
||||||
{% if "id_event" in field.label_tag %}
|
|
||||||
<div class="control-group{% if field.errors %} error{% endif%}">
|
|
||||||
<label for="id_{{ field.name }}">{{ field.label }}{% if field.field.required %}<span class="required">*</span>{% endif %}:</label>
|
|
||||||
{{ field }}
|
|
||||||
{% if field.errors %}
|
|
||||||
<span class="help-inline">{{ field.errors }}</span>
|
|
||||||
{% endif %}
|
|
||||||
{% if field.help_text %}
|
|
||||||
<span class="help-inline">{{ field.help_text }}</span>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</fieldset>
|
|
||||||
<p></p>
|
|
||||||
<fieldset>
|
|
||||||
<legend>{% trans "Welcome Widget" %}</legend>
|
|
||||||
{% for field in form %}
|
|
||||||
{% if "id_welcome" in field.label_tag %}
|
|
||||||
<div class="control-group{% if field.errors %} error{% endif%}">
|
|
||||||
<label for="id_{{ field.name }}">{{ field.label }}{% if field.field.required %}<span class="required">*</span>{% endif %}:</label>
|
|
||||||
{{ field }}
|
|
||||||
{% if field.errors %}
|
|
||||||
<span class="help-inline">{{ field.errors }}</span>
|
|
||||||
{% endif %}
|
|
||||||
{% if field.help_text %}
|
|
||||||
<span class="help-inline">{{ field.help_text }}</span>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</fieldset>
|
|
||||||
<p></p>
|
|
||||||
<fieldset style="width: 410px;">
|
|
||||||
<legend>{% trans "System" %}</legend>
|
|
||||||
{% for field in form %}
|
|
||||||
{% if "id_system" in field.label_tag %}
|
|
||||||
<div class="control-group{% if field.errors %} error{% endif%}">
|
|
||||||
<label for="id_{{ field.name }}">{{ field.label }}{% if field.field.required %}<span class="required">*</span>{% endif %}:</label>
|
|
||||||
{{ field }}
|
|
||||||
{% if field.errors %}
|
|
||||||
<span class="help-inline">{{ field.errors }}</span>
|
|
||||||
{% endif %}
|
|
||||||
{% if field.help_text %}
|
|
||||||
<span class="help-inline">{{ field.help_text }}</span>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</fieldset>
|
|
||||||
<p>
|
|
||||||
{% include "formbuttons_save.html" %}
|
|
||||||
<a href='{% url 'config_general' %}' class="btn">
|
|
||||||
{% trans 'Cancel' %}
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
<small>* {% trans "required" %}</small>
|
|
||||||
</form>
|
|
||||||
{% endblock %}
|
|
@ -1,15 +0,0 @@
|
|||||||
{% extends "config/base_config.html" %}
|
|
||||||
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block title %}{{ block.super }} – {% trans "Version" %}{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<h1>{% trans "Version" %}
|
|
||||||
{% block config_submenu %}{{ block.super }}{% endblock %}
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
{% for version in versions %}
|
|
||||||
<p>{{ version.0 }} {% trans "Version" %}: {{ version.1 }}</p>
|
|
||||||
{% endfor %}
|
|
||||||
{% endblock %}
|
|
@ -4,42 +4,28 @@
|
|||||||
openslides.config.urls
|
openslides.config.urls
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
URL list for the config app.
|
Url patterns for the config app.
|
||||||
|
|
||||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.conf.urls import patterns, url
|
from django.conf.urls import patterns, url
|
||||||
from django.utils.importlib import import_module
|
|
||||||
|
|
||||||
from openslides.config.views import GeneralConfig, VersionConfig
|
from openslides.utils.views import RedirectView
|
||||||
|
from .signals import config_signal
|
||||||
|
from .views import ConfigView
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
url(r'^general/$',
|
url(r'^$',
|
||||||
GeneralConfig.as_view(),
|
RedirectView.as_view(url_name='config_general'),
|
||||||
name='config_general',
|
name='config_first_config_page',
|
||||||
),
|
|
||||||
|
|
||||||
url(r'^version/$',
|
|
||||||
VersionConfig.as_view(),
|
|
||||||
name='config_version',
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
for app in settings.INSTALLED_APPS:
|
for receiver, config_page in config_signal.send(sender='config_urls'):
|
||||||
try:
|
if config_page.is_shown():
|
||||||
mod = import_module(app + '.views')
|
urlpatterns += patterns('', url(r'^%s/$' % config_page.url,
|
||||||
except ImportError:
|
ConfigView.as_view(config_page=config_page),
|
||||||
continue
|
name='config_%s' % config_page.url))
|
||||||
appname = mod.__name__.split('.')[-2]
|
|
||||||
try:
|
|
||||||
urlpatterns += patterns('', url(
|
|
||||||
r'^%s/$' % appname,
|
|
||||||
mod.Config.as_view(),
|
|
||||||
name='config_%s' % appname,
|
|
||||||
))
|
|
||||||
except AttributeError:
|
|
||||||
continue
|
|
||||||
|
|
||||||
|
@ -6,107 +6,126 @@
|
|||||||
|
|
||||||
Views for the config app.
|
Views for the config app.
|
||||||
|
|
||||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.conf import settings
|
from django import forms
|
||||||
from django.contrib import messages
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.utils.importlib import import_module
|
from django.contrib import messages
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from openslides import get_version, get_git_commit_id, RELEASE
|
from openslides.utils.views import FormView
|
||||||
from openslides.utils.template import Tab
|
from openslides.utils.template import Tab
|
||||||
from openslides.utils.views import FormView, TemplateView
|
from .api import config
|
||||||
from .forms import GeneralConfigForm
|
from .signals import config_signal
|
||||||
from .models import config
|
|
||||||
|
|
||||||
|
|
||||||
class GeneralConfig(FormView):
|
class ConfigView(FormView):
|
||||||
"""
|
"""
|
||||||
Gereral config values.
|
The view for a config page.
|
||||||
"""
|
"""
|
||||||
permission_required = 'config.can_manage_config'
|
template_name = 'config/config_form.html'
|
||||||
form_class = GeneralConfigForm
|
config_page = None
|
||||||
template_name = 'config/general.html'
|
form_class = forms.Form
|
||||||
success_url_name = 'config_general'
|
|
||||||
|
def has_permission(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Ensures that only users with tab's permission can see this view.
|
||||||
|
"""
|
||||||
|
self.permission_required = self.config_page.required_permission
|
||||||
|
return super(ConfigView, self).has_permission(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_form(self, *args):
|
||||||
|
"""
|
||||||
|
Gets the form for the view. Includes all form fields given by the
|
||||||
|
tab's config objects.
|
||||||
|
"""
|
||||||
|
form = super(ConfigView, self).get_form(*args)
|
||||||
|
for name, field in self.generate_form_fields_from_config_page():
|
||||||
|
form.fields[name] = field
|
||||||
|
return form
|
||||||
|
|
||||||
|
def generate_form_fields_from_config_page(self):
|
||||||
|
"""
|
||||||
|
Generates the fields for the get_form function.
|
||||||
|
"""
|
||||||
|
for variable in self.config_page.variables:
|
||||||
|
if variable.form_field is not None:
|
||||||
|
yield (variable.name, variable.form_field)
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
return {
|
"""
|
||||||
'event_name': config['event_name'],
|
Returns a dictonary with the actual values of the config variables
|
||||||
'event_description': config['event_description'],
|
as intial value for the form.
|
||||||
'event_date': config['event_date'],
|
"""
|
||||||
'event_location': config['event_location'],
|
initial = super(ConfigView, self).get_initial()
|
||||||
'event_organizer': config['event_organizer'],
|
for variable in self.config_page.variables:
|
||||||
'welcome_title': config['welcome_title'],
|
initial.update({variable.name: config[variable.name]})
|
||||||
'welcome_text': config['welcome_text'],
|
return initial
|
||||||
'system_enable_anonymous': config['system_enable_anonymous'],
|
|
||||||
}
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
# event
|
|
||||||
config['event_name'] = form.cleaned_data['event_name']
|
|
||||||
config['event_description'] = form.cleaned_data['event_description']
|
|
||||||
config['event_date'] = form.cleaned_data['event_date']
|
|
||||||
config['event_location'] = form.cleaned_data['event_location']
|
|
||||||
config['event_organizer'] = form.cleaned_data['event_organizer']
|
|
||||||
|
|
||||||
# welcome widget
|
|
||||||
config['welcome_title'] = form.cleaned_data['welcome_title']
|
|
||||||
config['welcome_text'] = form.cleaned_data['welcome_text']
|
|
||||||
|
|
||||||
# system
|
|
||||||
if form.cleaned_data['system_enable_anonymous']:
|
|
||||||
config['system_enable_anonymous'] = True
|
|
||||||
else:
|
|
||||||
config['system_enable_anonymous'] = False
|
|
||||||
|
|
||||||
messages.success(
|
|
||||||
self.request, _('General settings successfully saved.'))
|
|
||||||
return super(GeneralConfig, self).form_valid(form)
|
|
||||||
|
|
||||||
|
|
||||||
class VersionConfig(TemplateView):
|
|
||||||
"""
|
|
||||||
Show version infos.
|
|
||||||
"""
|
|
||||||
permission_required = 'config.can_manage_config'
|
|
||||||
template_name = 'config/version.html'
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(VersionConfig, self).get_context_data(**kwargs)
|
"""
|
||||||
|
Adds to the context the active config tab, a list of dictionaries
|
||||||
|
containing all config tabs each with a flag which is true if the
|
||||||
|
tab is the active one and adds a flag whether the config page has
|
||||||
|
groups. Adds also extra_stylefiles and extra_javascript.
|
||||||
|
"""
|
||||||
|
context = super(ConfigView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
# OpenSlides version. During development the git commit id is added.
|
context['active_config_page'] = self.config_page
|
||||||
openslides_version_string = get_version()
|
|
||||||
if not RELEASE:
|
config_pages_list = []
|
||||||
openslides_version_string += ' Commit: %s' % get_git_commit_id()
|
for receiver, config_page in config_signal.send(sender=self):
|
||||||
context['versions'] = [('OpenSlides', openslides_version_string)]
|
if config_page.is_shown():
|
||||||
|
config_pages_list.append({
|
||||||
|
'config_page': config_page,
|
||||||
|
'active': self.request.path == reverse('config_%s' % config_page.url)})
|
||||||
|
context['config_pages_list'] = sorted(config_pages_list, key=lambda config_page_dict: config_page_dict['config_page'].weight)
|
||||||
|
|
||||||
|
if hasattr(self.config_page, 'groups'):
|
||||||
|
context['groups'] = self.config_page.groups
|
||||||
|
else:
|
||||||
|
context['groups'] = None
|
||||||
|
|
||||||
|
if 'extra_stylefiles' in self.config_page.extra_context:
|
||||||
|
if 'extra_stylefiles' in context:
|
||||||
|
context['extra_stylefiles'].extend(self.config_page.extra_context['extra_stylefiles'])
|
||||||
|
else:
|
||||||
|
context['extra_stylefiles'] = self.config_page.extra_context['extra_stylefiles']
|
||||||
|
|
||||||
|
if 'extra_javascript' in self.config_page.extra_context:
|
||||||
|
if 'extra_javascript' in context:
|
||||||
|
context['extra_javascript'].extend(self.config_page.extra_context['extra_javascript'])
|
||||||
|
else:
|
||||||
|
context['extra_javascript'] = self.config_page.extra_context['extra_javascript']
|
||||||
|
|
||||||
# Version of plugins.
|
|
||||||
for plugin in settings.INSTALLED_PLUGINS:
|
|
||||||
try:
|
|
||||||
mod = import_module(plugin)
|
|
||||||
plugin_version = get_version(mod.VERSION)
|
|
||||||
except (ImportError, AttributeError, AssertionError):
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
plugin_name = mod.NAME
|
|
||||||
except AttributeError:
|
|
||||||
plugin_name = mod.__name__.split('.')[0]
|
|
||||||
context['versions'].append((plugin_name, plugin_version))
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
"""
|
||||||
|
Returns the success url when changes are saved. Here it is the same
|
||||||
|
url as the tab.
|
||||||
|
"""
|
||||||
|
return reverse('config_%s' % self.config_page.url)
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
"""
|
||||||
|
Saves all data of a valid form.
|
||||||
|
"""
|
||||||
|
for key in form.cleaned_data:
|
||||||
|
config[key] = form.cleaned_data[key]
|
||||||
|
messages.success(self.request, _('%s settings successfully saved.' % self.config_page.title))
|
||||||
|
return super(ConfigView, self).form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
def register_tab(request):
|
def register_tab(request):
|
||||||
"""
|
"""
|
||||||
Register the config tab.
|
Registers the tab for this app in the main menu.
|
||||||
"""
|
"""
|
||||||
selected = request.path.startswith('/config/')
|
|
||||||
return Tab(
|
return Tab(
|
||||||
title=_('Configuration'),
|
title=_('Configuration'),
|
||||||
app='config',
|
app='config',
|
||||||
url=reverse('config_general'),
|
url=reverse('config_first_config_page'),
|
||||||
permission=request.user.has_perm('config.can_manage_config'),
|
permission=request.user.has_perm('config.can_manage'),
|
||||||
selected=selected,
|
selected=request.path.startswith('/config/'))
|
||||||
)
|
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
openslides.core
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The core app.
|
||||||
|
|
||||||
|
:copyright: (c) 2011–2013 by the OpenSlides team, see AUTHORS.
|
||||||
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from . import signals
|
@ -2,15 +2,110 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
openslides.core.signals
|
openslides.core.signals
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Core Signals.
|
Signals for the core app.
|
||||||
|
|
||||||
:copyright: (c) 2011-2013 by the OpenSlides team, see AUTHORS.
|
:copyright: (c) 2011–2013 by the OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.dispatch import Signal
|
from django.dispatch import Signal, receiver
|
||||||
|
from django import forms
|
||||||
|
from django.utils.translation import ugettext_lazy, ugettext_noop, ugettext as _
|
||||||
|
|
||||||
|
from openslides.config.signals import config_signal
|
||||||
|
from openslides.config.api import ConfigVariable, ConfigGroup, ConfigGroupedPage
|
||||||
|
|
||||||
|
|
||||||
post_database_setup = Signal()
|
post_database_setup = Signal()
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(config_signal, dispatch_uid='setup_general_config_page')
|
||||||
|
def setup_general_config_page(sender, **kwargs):
|
||||||
|
"""
|
||||||
|
General config variables for OpenSlides. They are grouped in 'Event',
|
||||||
|
'Welcome Widget' and 'System'.
|
||||||
|
"""
|
||||||
|
event_name = ConfigVariable(
|
||||||
|
name='event_name',
|
||||||
|
default_value='OpenSlides',
|
||||||
|
form_field=forms.CharField(
|
||||||
|
widget=forms.TextInput(),
|
||||||
|
label=ugettext_lazy('Event name'),
|
||||||
|
max_length=30))
|
||||||
|
|
||||||
|
event_description = ConfigVariable(
|
||||||
|
name='event_description',
|
||||||
|
default_value=_('Presentation and assembly system'),
|
||||||
|
form_field=forms.CharField(
|
||||||
|
widget=forms.TextInput(),
|
||||||
|
label=ugettext_lazy('Short description of event'),
|
||||||
|
required=False,
|
||||||
|
max_length=100))
|
||||||
|
|
||||||
|
event_date = ConfigVariable(
|
||||||
|
name='event_date',
|
||||||
|
default_value='',
|
||||||
|
form_field=forms.CharField(
|
||||||
|
widget=forms.TextInput(),
|
||||||
|
label=ugettext_lazy('Event date'),
|
||||||
|
required=False))
|
||||||
|
|
||||||
|
event_location = ConfigVariable(
|
||||||
|
name='event_location',
|
||||||
|
default_value='',
|
||||||
|
form_field=forms.CharField(
|
||||||
|
widget=forms.TextInput(),
|
||||||
|
label=ugettext_lazy('Event location'),
|
||||||
|
required=False))
|
||||||
|
|
||||||
|
event_organizer = ConfigVariable(
|
||||||
|
name='event_organizer',
|
||||||
|
default_value='',
|
||||||
|
form_field=forms.CharField(
|
||||||
|
widget=forms.TextInput(),
|
||||||
|
label=ugettext_lazy('Event organizer'),
|
||||||
|
required=False))
|
||||||
|
|
||||||
|
welcome_title = ConfigVariable(
|
||||||
|
name='welcome_title',
|
||||||
|
default_value=_('Welcome to OpenSlides'),
|
||||||
|
form_field=forms.CharField(
|
||||||
|
widget=forms.TextInput(),
|
||||||
|
label=ugettext_lazy('Title'),
|
||||||
|
required=False))
|
||||||
|
|
||||||
|
welcome_text = ConfigVariable(
|
||||||
|
name='welcome_text',
|
||||||
|
default_value=_('[Place for your welcome text.]'),
|
||||||
|
form_field=forms.CharField(
|
||||||
|
widget=forms.Textarea(),
|
||||||
|
label=ugettext_lazy('Welcome text'),
|
||||||
|
required=False))
|
||||||
|
|
||||||
|
system_enable_anonymous = ConfigVariable(
|
||||||
|
name='system_enable_anonymous',
|
||||||
|
default_value=False,
|
||||||
|
form_field=forms.BooleanField(
|
||||||
|
label=ugettext_lazy('Allow access for anonymous guest users'),
|
||||||
|
required=False))
|
||||||
|
|
||||||
|
group_event = ConfigGroup(
|
||||||
|
title=ugettext_lazy('Event'),
|
||||||
|
variables=(event_name, event_description, event_date, event_location, event_organizer))
|
||||||
|
|
||||||
|
group_welcome_widget = ConfigGroup(
|
||||||
|
title=ugettext_lazy('Welcome Widget'),
|
||||||
|
variables=(welcome_title, welcome_text))
|
||||||
|
|
||||||
|
group_system = ConfigGroup(
|
||||||
|
title=ugettext_lazy('System'),
|
||||||
|
variables=(system_enable_anonymous,))
|
||||||
|
|
||||||
|
return ConfigGroupedPage(
|
||||||
|
title=ugettext_noop('General'),
|
||||||
|
url='general',
|
||||||
|
required_permission='config.can_manage',
|
||||||
|
weight=10,
|
||||||
|
groups=(group_event, group_welcome_widget, group_system))
|
||||||
|
12
openslides/core/templates/core/version.html
Normal file
12
openslides/core/templates/core/version.html
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block title %}{{ block.super }} – {% trans 'Version' %}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>{% trans 'Versions' %}</h1>
|
||||||
|
{% for version in versions %}
|
||||||
|
<p>{{ version.0 }} {% trans "Version" %}: {{ version.1 }}</p>
|
||||||
|
{% endfor %}
|
||||||
|
{% endblock %}
|
29
openslides/core/urls.py
Normal file
29
openslides/core/urls.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
openslides.core.urls
|
||||||
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Url patterns for the core app.
|
||||||
|
|
||||||
|
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||||
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.conf.urls import patterns, url
|
||||||
|
|
||||||
|
from openslides.utils.views import RedirectView
|
||||||
|
from .views import VersionView
|
||||||
|
|
||||||
|
|
||||||
|
urlpatterns = patterns('',
|
||||||
|
|
||||||
|
# Redirect to dashboard URL
|
||||||
|
url(r'^$',
|
||||||
|
RedirectView.as_view(url='projector/dashboard/'),
|
||||||
|
name='home',),
|
||||||
|
|
||||||
|
url(r'^version/$',
|
||||||
|
VersionView.as_view(),
|
||||||
|
name='core_version',),
|
||||||
|
)
|
51
openslides/core/views.py
Normal file
51
openslides/core/views.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
openslides.core.views
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Views for the core app.
|
||||||
|
|
||||||
|
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||||
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.utils.importlib import import_module
|
||||||
|
|
||||||
|
from openslides import get_version, get_git_commit_id, RELEASE
|
||||||
|
from openslides.utils.views import TemplateView
|
||||||
|
|
||||||
|
|
||||||
|
class VersionView(TemplateView):
|
||||||
|
"""
|
||||||
|
Show version infos.
|
||||||
|
"""
|
||||||
|
template_name = 'core/version.html'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
"""
|
||||||
|
Adds version strings to the context.
|
||||||
|
"""
|
||||||
|
context = super(VersionView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
# OpenSlides version. During development the git commit id is added.
|
||||||
|
openslides_version_string = get_version()
|
||||||
|
if not RELEASE:
|
||||||
|
openslides_version_string += ' Commit: %s' % get_git_commit_id()
|
||||||
|
context['versions'] = [('OpenSlides', openslides_version_string)]
|
||||||
|
|
||||||
|
# Versions of plugins.
|
||||||
|
for plugin in settings.INSTALLED_PLUGINS:
|
||||||
|
try:
|
||||||
|
mod = import_module(plugin)
|
||||||
|
plugin_version = get_version(mod.VERSION)
|
||||||
|
except (ImportError, AttributeError, AssertionError):
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
plugin_name = mod.NAME
|
||||||
|
except AttributeError:
|
||||||
|
plugin_name = mod.__name__.split('.')[0]
|
||||||
|
context['versions'].append((plugin_name, plugin_version))
|
||||||
|
|
||||||
|
return context
|
@ -89,6 +89,7 @@ MIDDLEWARE_CLASSES = (
|
|||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
'openslides.participant.middleware.AuthenticationMiddleware',
|
'openslides.participant.middleware.AuthenticationMiddleware',
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
|
'openslides.config.middleware.ConfigCacheMiddleware',
|
||||||
)
|
)
|
||||||
|
|
||||||
ROOT_URLCONF = 'openslides.urls'
|
ROOT_URLCONF = 'openslides.urls'
|
||||||
@ -110,6 +111,7 @@ INSTALLED_APPS = (
|
|||||||
'mptt',
|
'mptt',
|
||||||
'openslides.utils',
|
'openslides.utils',
|
||||||
'openslides.poll',
|
'openslides.poll',
|
||||||
|
'openslides.core',
|
||||||
'openslides.projector',
|
'openslides.projector',
|
||||||
'openslides.agenda',
|
'openslides.agenda',
|
||||||
'openslides.motion',
|
'openslides.motion',
|
||||||
|
@ -284,12 +284,10 @@ def run_syncdb():
|
|||||||
def set_system_url(url):
|
def set_system_url(url):
|
||||||
# can't be imported in global scope as it already requires
|
# can't be imported in global scope as it already requires
|
||||||
# the settings module during import
|
# the settings module during import
|
||||||
from openslides.config.models import config
|
from openslides.config.api import config
|
||||||
|
|
||||||
key = "participant_pdf_system_url"
|
if config['participant_pdf_system_url'] == 'http://example.com:8000':
|
||||||
if key in config:
|
config['participant_pdf_system_url'] = url
|
||||||
return
|
|
||||||
config[key] = url
|
|
||||||
|
|
||||||
|
|
||||||
def create_or_reset_admin_user():
|
def create_or_reset_admin_user():
|
||||||
|
@ -7,9 +7,8 @@
|
|||||||
The OpenSlides motion app appends the functionality to OpenSlides to
|
The OpenSlides motion app appends the functionality to OpenSlides to
|
||||||
manage motions.
|
manage motions.
|
||||||
|
|
||||||
:copyright: (c) 2011-2013 by the OpenSlides team, see AUTHORS.
|
:copyright: (c) 2011–2013 by the OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import openslides.motion.signals
|
from . import signals, slides
|
||||||
import openslides.motion.slides
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
Exceptions for the motion app.
|
Exceptions for the motion app.
|
||||||
|
|
||||||
:copyright: (c) 2011-2013 by the OpenSlides team, see AUTHORS.
|
:copyright: (c) 2011–2013 by the OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ from django.utils.translation import ugettext as _
|
|||||||
|
|
||||||
from openslides.utils.forms import CssClassMixin
|
from openslides.utils.forms import CssClassMixin
|
||||||
from openslides.utils.person import PersonFormField, MultiplePersonFormField
|
from openslides.utils.person import PersonFormField, MultiplePersonFormField
|
||||||
from .models import Motion, Workflow, Category
|
from .models import Motion, Category
|
||||||
|
|
||||||
|
|
||||||
class BaseMotionForm(forms.ModelForm, CssClassMixin):
|
class BaseMotionForm(forms.ModelForm, CssClassMixin):
|
||||||
@ -100,55 +100,3 @@ class MotionIdentifierMixin(forms.ModelForm):
|
|||||||
"""Mixin to let the user choose the identifier for the motion."""
|
"""Mixin to let the user choose the identifier for the motion."""
|
||||||
|
|
||||||
identifier = forms.CharField(required=False)
|
identifier = forms.CharField(required=False)
|
||||||
|
|
||||||
|
|
||||||
class ConfigForm(CssClassMixin, forms.Form):
|
|
||||||
"""Form for the configuration tab of OpenSlides."""
|
|
||||||
motion_min_supporters = forms.IntegerField(
|
|
||||||
widget=forms.TextInput(attrs={'class': 'small-input'}),
|
|
||||||
label=_("Number of (minimum) required supporters for a motion"),
|
|
||||||
initial=4, min_value=0, max_value=8,
|
|
||||||
help_text=_("Choose 0 to disable the supporting system"),
|
|
||||||
)
|
|
||||||
motion_preamble = forms.CharField(
|
|
||||||
widget=forms.TextInput(),
|
|
||||||
required=False,
|
|
||||||
label=_("Motion preamble")
|
|
||||||
)
|
|
||||||
motion_pdf_ballot_papers_selection = forms.ChoiceField(
|
|
||||||
widget=forms.Select(),
|
|
||||||
required=False,
|
|
||||||
label=_("Number of ballot papers (selection)"),
|
|
||||||
choices=[
|
|
||||||
("NUMBER_OF_DELEGATES", _("Number of all delegates")),
|
|
||||||
("NUMBER_OF_ALL_PARTICIPANTS", _("Number of all participants")),
|
|
||||||
("CUSTOM_NUMBER", _("Use the following custom number")),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
motion_pdf_ballot_papers_number = forms.IntegerField(
|
|
||||||
widget=forms.TextInput(attrs={'class': 'small-input'}),
|
|
||||||
required=False,
|
|
||||||
min_value=1,
|
|
||||||
label=_("Custom number of ballot papers")
|
|
||||||
)
|
|
||||||
motion_pdf_title = forms.CharField(
|
|
||||||
widget=forms.TextInput(),
|
|
||||||
required=False,
|
|
||||||
label=_("Title for PDF document (all motions)")
|
|
||||||
)
|
|
||||||
motion_pdf_preamble = forms.CharField(
|
|
||||||
widget=forms.Textarea(),
|
|
||||||
required=False,
|
|
||||||
label=_("Preamble text for PDF document (all motions)")
|
|
||||||
)
|
|
||||||
|
|
||||||
motion_allow_disable_versioning = forms.BooleanField(
|
|
||||||
label=_("Allow to disable versioning"),
|
|
||||||
required=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
motion_workflow = forms.ChoiceField(
|
|
||||||
widget=forms.Select(),
|
|
||||||
label=_("Workflow of new motions"),
|
|
||||||
required=True,
|
|
||||||
choices=[(workflow.pk, workflow.name) for workflow in Workflow.objects.all()])
|
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
To use a motion object, you only have to import the Motion class. Any
|
To use a motion object, you only have to import the Motion class. Any
|
||||||
functionality can be reached from a motion object.
|
functionality can be reached from a motion object.
|
||||||
|
|
||||||
:copyright: (c) 2011-2013 by the OpenSlides team, see AUTHORS.
|
:copyright: (c) 2011–2013 by the OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -24,8 +24,7 @@ from django.utils.translation import pgettext
|
|||||||
from django.utils.translation import ugettext_lazy, ugettext_noop, ugettext as _
|
from django.utils.translation import ugettext_lazy, ugettext_noop, ugettext as _
|
||||||
|
|
||||||
from openslides.utils.person import PersonField
|
from openslides.utils.person import PersonField
|
||||||
from openslides.config.models import config
|
from openslides.config.api import config
|
||||||
from openslides.config.signals import default_config_value
|
|
||||||
from openslides.poll.models import (
|
from openslides.poll.models import (
|
||||||
BaseOption, BasePoll, CountVotesCast, CountInvalid, BaseVote)
|
BaseOption, BasePoll, CountVotesCast, CountInvalid, BaseVote)
|
||||||
from openslides.participant.models import User
|
from openslides.participant.models import User
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
Functions to generate the PDFs for the motion app.
|
Functions to generate the PDFs for the motion app.
|
||||||
|
|
||||||
:copyright: (c) 2011-2013 by the OpenSlides team, see AUTHORS.
|
:copyright: (c) 2011–2013 by the OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -16,9 +16,8 @@ from reportlab.platypus import (
|
|||||||
SimpleDocTemplate, PageBreak, Paragraph, Spacer, Table, TableStyle)
|
SimpleDocTemplate, PageBreak, Paragraph, Spacer, Table, TableStyle)
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from openslides.config.models import config
|
from openslides.config.api import config
|
||||||
from openslides.utils.pdf import stylesheet
|
from openslides.utils.pdf import stylesheet
|
||||||
|
|
||||||
from .models import Motion
|
from .models import Motion
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,26 +11,110 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.utils.translation import ugettext as _, ugettext_noop
|
from django import forms
|
||||||
|
from django.utils.translation import ugettext as _, ugettext_lazy, ugettext_noop
|
||||||
|
|
||||||
from openslides.config.signals import default_config_value
|
from openslides.config.signals import config_signal
|
||||||
|
from openslides.config.api import ConfigVariable, ConfigPage
|
||||||
from openslides.core.signals import post_database_setup
|
from openslides.core.signals import post_database_setup
|
||||||
|
|
||||||
from .models import Workflow, State
|
from .models import Workflow, State
|
||||||
|
|
||||||
|
|
||||||
@receiver(default_config_value, dispatch_uid="motion_default_config")
|
@receiver(config_signal, dispatch_uid='setup_motion_config_page')
|
||||||
def default_config(sender, key, **kwargs):
|
def setup_motion_config_page(sender, **kwargs):
|
||||||
"""Return the default config values for the motion app."""
|
"""
|
||||||
return {
|
Motion config variables.
|
||||||
'motion_min_supporters': 0,
|
"""
|
||||||
'motion_preamble': _('The assembly may decide,'),
|
motion_min_supporters = ConfigVariable(
|
||||||
'motion_pdf_ballot_papers_selection': 'CUSTOM_NUMBER',
|
name='motion_min_supporters',
|
||||||
'motion_pdf_ballot_papers_number': '8',
|
default_value=0,
|
||||||
'motion_pdf_title': _('Motions'),
|
form_field=forms.IntegerField(
|
||||||
'motion_pdf_preamble': '',
|
widget=forms.TextInput(attrs={'class': 'small-input'}),
|
||||||
'motion_allow_disable_versioning': False,
|
label=_('Number of (minimum) required supporters for a motion'),
|
||||||
'motion_workflow': 1}.get(key)
|
initial=4,
|
||||||
|
min_value=0,
|
||||||
|
max_value=8,
|
||||||
|
help_text=_('Choose 0 to disable the supporting system')))
|
||||||
|
motion_preamble = ConfigVariable(
|
||||||
|
name='motion_preamble',
|
||||||
|
default_value=_('The assembly may decide,'),
|
||||||
|
form_field=forms.CharField(
|
||||||
|
widget=forms.TextInput(),
|
||||||
|
required=False,
|
||||||
|
label=_('Motion preamble')))
|
||||||
|
motion_pdf_ballot_papers_selection = ConfigVariable(
|
||||||
|
name='motion_pdf_ballot_papers_selection',
|
||||||
|
default_value='CUSTOM_NUMBER',
|
||||||
|
form_field=forms.ChoiceField(
|
||||||
|
widget=forms.Select(),
|
||||||
|
required=False,
|
||||||
|
label=_('Number of ballot papers (selection)'),
|
||||||
|
choices=[
|
||||||
|
('NUMBER_OF_DELEGATES', _('Number of all delegates')),
|
||||||
|
('NUMBER_OF_ALL_PARTICIPANTS', _('Number of all participants')),
|
||||||
|
('CUSTOM_NUMBER', _("Use the following custom number"))]))
|
||||||
|
motion_pdf_ballot_papers_number = ConfigVariable(
|
||||||
|
name='motion_pdf_ballot_papers_number',
|
||||||
|
default_value=8,
|
||||||
|
form_field=forms.IntegerField(
|
||||||
|
widget=forms.TextInput(attrs={'class': 'small-input'}),
|
||||||
|
required=False,
|
||||||
|
min_value=1,
|
||||||
|
label=_('Custom number of ballot papers')))
|
||||||
|
motion_pdf_title = ConfigVariable(
|
||||||
|
name='motion_pdf_title',
|
||||||
|
default_value=_('Motions'),
|
||||||
|
form_field=forms.CharField(
|
||||||
|
widget=forms.TextInput(),
|
||||||
|
required=False,
|
||||||
|
label=_('Title for PDF document (all motions)')))
|
||||||
|
motion_pdf_preamble = ConfigVariable(
|
||||||
|
name='motion_pdf_preamble',
|
||||||
|
default_value='',
|
||||||
|
form_field=forms.CharField(
|
||||||
|
widget=forms.Textarea(),
|
||||||
|
required=False,
|
||||||
|
label=_('Preamble text for PDF document (all motions)')))
|
||||||
|
motion_allow_disable_versioning = ConfigVariable(
|
||||||
|
name='motion_allow_disable_versioning',
|
||||||
|
default_value=False,
|
||||||
|
form_field=forms.BooleanField(
|
||||||
|
label=_('Allow to disable versioning'),
|
||||||
|
required=False))
|
||||||
|
motion_workflow = ConfigVariable(
|
||||||
|
name='motion_workflow',
|
||||||
|
default_value=1,
|
||||||
|
form_field=forms.ChoiceField(
|
||||||
|
widget=forms.Select(),
|
||||||
|
label=_('Workflow of new motions'),
|
||||||
|
required=True,
|
||||||
|
choices=[(workflow.pk, workflow.name) for workflow in Workflow.objects.all()]))
|
||||||
|
motion_identifier = ConfigVariable(
|
||||||
|
name='motion_identifier',
|
||||||
|
default_value='manually',
|
||||||
|
form_field=forms.ChoiceField(
|
||||||
|
widget=forms.Select(),
|
||||||
|
required=False,
|
||||||
|
label=_('Identifier'),
|
||||||
|
choices=[
|
||||||
|
('manually', _('Set it manually')),
|
||||||
|
('per_category', _('Numbered per category')),
|
||||||
|
('serially_numbered', _('Serially numbered'))]))
|
||||||
|
|
||||||
|
return ConfigPage(title=ugettext_noop('Motion'),
|
||||||
|
url='motion',
|
||||||
|
required_permission='config.can_manage',
|
||||||
|
weight=30,
|
||||||
|
variables=(motion_min_supporters,
|
||||||
|
motion_preamble,
|
||||||
|
motion_pdf_ballot_papers_selection,
|
||||||
|
motion_pdf_ballot_papers_number,
|
||||||
|
motion_pdf_title,
|
||||||
|
motion_pdf_preamble,
|
||||||
|
motion_allow_disable_versioning,
|
||||||
|
motion_workflow,
|
||||||
|
motion_identifier))
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_database_setup, dispatch_uid='motion_create_builtin_workflows')
|
@receiver(post_database_setup, dispatch_uid='motion_create_builtin_workflows')
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
Defines the slides for the motion app.
|
Defines the slides for the motion app.
|
||||||
|
|
||||||
:copyright: (c) 2011-2013 by the OpenSlides team, see AUTHORS.
|
:copyright: (c) 2011–2013 by the OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
{% extends "config/base_config.html" %}
|
|
||||||
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block title %}{{ block.super }} – {% trans "Motion settings" %}{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<h1>
|
|
||||||
{% trans "Configuration" %}
|
|
||||||
<small>{% trans "Motions" %}</small>
|
|
||||||
{% block config_submenu %}{{ block.super }}{% endblock %}
|
|
||||||
</h1>
|
|
||||||
<form action="" method="post">{% csrf_token %}
|
|
||||||
{% include "form.html" %}
|
|
||||||
<p>
|
|
||||||
{% include "formbuttons_save.html" %}
|
|
||||||
<a href="{% url 'config_motion' %}" class="btn">
|
|
||||||
{% trans 'Cancel' %}
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
<small>* {% trans "required" %}</small>
|
|
||||||
</form>
|
|
||||||
{% endblock %}
|
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
Defines the URL patterns for the motion app.
|
Defines the URL patterns for the motion app.
|
||||||
|
|
||||||
:copyright: (c) 2011-2013 by the OpenSlides team, see AUTHORS.
|
:copyright: (c) 2011–2013 by the OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -29,21 +29,17 @@ from openslides.utils.utils import html_strong, htmldiff
|
|||||||
from openslides.poll.views import PollFormView
|
from openslides.poll.views import PollFormView
|
||||||
from openslides.projector.api import get_active_slide
|
from openslides.projector.api import get_active_slide
|
||||||
from openslides.projector.projector import Widget, SLIDE
|
from openslides.projector.projector import Widget, SLIDE
|
||||||
from openslides.config.models import config
|
from openslides.config.api import config
|
||||||
from openslides.agenda.models import Item
|
from openslides.agenda.models import Item
|
||||||
|
|
||||||
from .models import (Motion, MotionSubmitter, MotionSupporter, MotionPoll,
|
from .models import (Motion, MotionSubmitter, MotionSupporter, MotionPoll,
|
||||||
MotionVersion, State, WorkflowError, Category)
|
MotionVersion, State, WorkflowError, Category)
|
||||||
from .forms import (BaseMotionForm, MotionSubmitterMixin, MotionSupporterMixin,
|
from .forms import (BaseMotionForm, MotionSubmitterMixin, MotionSupporterMixin,
|
||||||
MotionDisableVersioningMixin, ConfigForm, MotionCategoryMixin,
|
MotionDisableVersioningMixin, MotionCategoryMixin,
|
||||||
MotionIdentifierMixin)
|
MotionIdentifierMixin)
|
||||||
from .pdf import motions_to_pdf, motion_to_pdf
|
from .pdf import motions_to_pdf, motion_to_pdf
|
||||||
|
|
||||||
|
|
||||||
# TODO: into the config-tab
|
|
||||||
config['motion_identifier'] = ('manually', 'per_category', 'serially_numbered')[2]
|
|
||||||
|
|
||||||
|
|
||||||
class MotionListView(ListView):
|
class MotionListView(ListView):
|
||||||
"""View, to list all motions."""
|
"""View, to list all motions."""
|
||||||
permission_required = 'motion.can_see_motion'
|
permission_required = 'motion.can_see_motion'
|
||||||
@ -590,49 +586,15 @@ class CategoryDeleteView(DeleteView):
|
|||||||
category_delete = CategoryDeleteView.as_view()
|
category_delete = CategoryDeleteView.as_view()
|
||||||
|
|
||||||
|
|
||||||
class Config(FormView):
|
|
||||||
"""The View for the config tab."""
|
|
||||||
permission_required = 'config.can_manage_config'
|
|
||||||
form_class = ConfigForm
|
|
||||||
template_name = 'motion/config.html'
|
|
||||||
success_url_name = 'config_motion'
|
|
||||||
|
|
||||||
def get_initial(self):
|
|
||||||
return {
|
|
||||||
'motion_min_supporters': config['motion_min_supporters'],
|
|
||||||
'motion_preamble': config['motion_preamble'],
|
|
||||||
'motion_pdf_ballot_papers_selection': config['motion_pdf_ballot_papers_selection'],
|
|
||||||
'motion_pdf_ballot_papers_number': config['motion_pdf_ballot_papers_number'],
|
|
||||||
'motion_pdf_title': config['motion_pdf_title'],
|
|
||||||
'motion_pdf_preamble': config['motion_pdf_preamble'],
|
|
||||||
'motion_allow_disable_versioning': config['motion_allow_disable_versioning'],
|
|
||||||
'motion_workflow': config['motion_workflow'],
|
|
||||||
}
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
config['motion_min_supporters'] = form.cleaned_data['motion_min_supporters']
|
|
||||||
config['motion_preamble'] = form.cleaned_data['motion_preamble']
|
|
||||||
config['motion_pdf_ballot_papers_selection'] = form.cleaned_data['motion_pdf_ballot_papers_selection']
|
|
||||||
config['motion_pdf_ballot_papers_number'] = form.cleaned_data['motion_pdf_ballot_papers_number']
|
|
||||||
config['motion_pdf_title'] = form.cleaned_data['motion_pdf_title']
|
|
||||||
config['motion_pdf_preamble'] = form.cleaned_data['motion_pdf_preamble']
|
|
||||||
config['motion_allow_disable_versioning'] = form.cleaned_data['motion_allow_disable_versioning']
|
|
||||||
config['motion_workflow'] = form.cleaned_data['motion_workflow']
|
|
||||||
messages.success(self.request, _('Motion settings successfully saved.'))
|
|
||||||
return super(Config, self).form_valid(form)
|
|
||||||
|
|
||||||
|
|
||||||
def register_tab(request):
|
def register_tab(request):
|
||||||
"""Return the motion tab."""
|
"""Return the motion tab."""
|
||||||
# TODO: Find a bether way to set the selected var.
|
# TODO: Find a better way to set the selected var.
|
||||||
selected = request.path.startswith('/motion/')
|
|
||||||
return Tab(
|
return Tab(
|
||||||
title=_('Motions'),
|
title=_('Motions'),
|
||||||
app='motion',
|
app='motion',
|
||||||
url=reverse('motion_list'),
|
url=reverse('motion_list'),
|
||||||
permission=request.user.has_perm('motion.can_see_motion'),
|
permission=request.user.has_perm('motion.can_see_motion'),
|
||||||
selected=selected,
|
selected=request.path.startswith('/motion/'))
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_widgets(request):
|
def get_widgets(request):
|
||||||
|
@ -6,13 +6,13 @@
|
|||||||
|
|
||||||
The OpenSlides participant app.
|
The OpenSlides participant app.
|
||||||
|
|
||||||
:copyright: (c) 2011-2013 by the OpenSlides team, see AUTHORS.
|
:copyright: (c) 2011–2013 by the OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.utils.translation import ugettext_noop
|
from django.utils.translation import ugettext_noop
|
||||||
|
|
||||||
import openslides.participant.signals
|
from . import signals
|
||||||
|
|
||||||
|
|
||||||
NAME = ugettext_noop('Participant')
|
NAME = ugettext_noop('Participant')
|
||||||
|
@ -6,13 +6,13 @@
|
|||||||
|
|
||||||
Forms for the participant app.
|
Forms for the participant app.
|
||||||
|
|
||||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib.auth.models import Permission
|
from django.contrib.auth.models import Permission
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _ # TODO: Change this in the code
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from openslides.utils.forms import (
|
from openslides.utils.forms import (
|
||||||
@ -112,20 +112,3 @@ class UsersettingsForm(forms.ModelForm, CssClassMixin):
|
|||||||
class UserImportForm(forms.Form, CssClassMixin):
|
class UserImportForm(forms.Form, CssClassMixin):
|
||||||
csvfile = forms.FileField(widget=forms.FileInput(attrs={'size': '50'}),
|
csvfile = forms.FileField(widget=forms.FileInput(attrs={'size': '50'}),
|
||||||
label=_("CSV File"))
|
label=_("CSV File"))
|
||||||
|
|
||||||
|
|
||||||
class ConfigForm(forms.Form, CssClassMixin):
|
|
||||||
participant_pdf_system_url = forms.CharField(
|
|
||||||
widget=forms.TextInput(),
|
|
||||||
required=False,
|
|
||||||
label=_("System URL"),
|
|
||||||
help_text=_("Printed in PDF of first time passwords only."))
|
|
||||||
participant_pdf_welcometext = forms.CharField(
|
|
||||||
widget=forms.Textarea(),
|
|
||||||
required=False,
|
|
||||||
label=_("Welcome text"),
|
|
||||||
help_text=_("Printed in PDF of first time passwords only."))
|
|
||||||
participant_sort_users_by_first_name = forms.BooleanField(
|
|
||||||
required=False,
|
|
||||||
label=_("Sort participants by first name"),
|
|
||||||
help_text=_("Disable for sorting by last name"))
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
Models for the participant app.
|
Models for the participant app.
|
||||||
|
|
||||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -14,14 +14,11 @@ from django.contrib.auth.models import User as DjangoUser, Group as DjangoGroup
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import signals
|
from django.db.models import signals
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.utils.translation import ugettext_lazy as _, ugettext_noop
|
from django.utils.translation import ugettext_lazy as _, ugettext_noop # TODO: Change this in the code
|
||||||
|
|
||||||
from openslides.utils.person import PersonMixin, Person
|
from openslides.utils.person import PersonMixin, Person
|
||||||
from openslides.utils.person.signals import receive_persons
|
from openslides.utils.person.signals import receive_persons
|
||||||
|
from openslides.config.api import config
|
||||||
from openslides.config.models import config
|
|
||||||
from openslides.config.signals import default_config_value
|
|
||||||
|
|
||||||
from openslides.projector.api import register_slidemodel
|
from openslides.projector.api import register_slidemodel
|
||||||
from openslides.projector.projector import SlideMixin
|
from openslides.projector.projector import SlideMixin
|
||||||
|
|
||||||
@ -226,19 +223,6 @@ def receive_persons(sender, **kwargs):
|
|||||||
id_filter=kwargs['id_filter'])
|
id_filter=kwargs['id_filter'])
|
||||||
|
|
||||||
|
|
||||||
@receiver(default_config_value, dispatch_uid="participant_default_config")
|
|
||||||
def default_config(sender, key, **kwargs):
|
|
||||||
"""
|
|
||||||
Default values for the participant app.
|
|
||||||
"""
|
|
||||||
# TODO: Rename config-vars
|
|
||||||
return {
|
|
||||||
'participant_pdf_system_url': 'http://example.com:8000',
|
|
||||||
'participant_pdf_welcometext': _('Welcome to OpenSlides!'),
|
|
||||||
'participant_sort_users_by_first_name': False,
|
|
||||||
}.get(key)
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(signals.post_save, sender=DjangoUser)
|
@receiver(signals.post_save, sender=DjangoUser)
|
||||||
def djangouser_post_save(sender, instance, signal, *args, **kwargs):
|
def djangouser_post_save(sender, instance, signal, *args, **kwargs):
|
||||||
try:
|
try:
|
||||||
|
@ -2,24 +2,66 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
openslides.participant.signals
|
openslides.participant.signals
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Signals for the participant app.
|
Signals for the participant app.
|
||||||
|
|
||||||
:copyright: (c) 2011-2013 by the OpenSlides team, see AUTHORS.
|
:copyright: (c) 2011–2013 by the OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.utils.translation import ugettext_noop
|
from django import forms
|
||||||
|
from django.utils.translation import ugettext_noop, ugettext_lazy, ugettext as _
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.contrib.auth.models import Permission
|
from django.contrib.auth.models import Permission
|
||||||
|
|
||||||
from openslides.core.signals import post_database_setup
|
from openslides.core.signals import post_database_setup
|
||||||
|
from openslides.config.signals import config_signal
|
||||||
|
from openslides.config.api import ConfigVariable, ConfigPage
|
||||||
|
|
||||||
from .models import Group
|
from .models import Group
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(config_signal, dispatch_uid='setup_participant_config_page')
|
||||||
|
def setup_participant_config_page(sender, **kwargs):
|
||||||
|
"""
|
||||||
|
Participant config variables.
|
||||||
|
"""
|
||||||
|
# TODO: Rename config-vars
|
||||||
|
participant_pdf_system_url = ConfigVariable(
|
||||||
|
name='participant_pdf_system_url',
|
||||||
|
default_value='http://example.com:8000',
|
||||||
|
form_field=forms.CharField(
|
||||||
|
widget=forms.TextInput(),
|
||||||
|
required=False,
|
||||||
|
label=_('System URL'),
|
||||||
|
help_text=_('Printed in PDF of first time passwords only.')))
|
||||||
|
participant_pdf_welcometext = ConfigVariable(
|
||||||
|
name='participant_pdf_welcometext',
|
||||||
|
default_value=_('Welcome to OpenSlides!'),
|
||||||
|
form_field=forms.CharField(
|
||||||
|
widget=forms.Textarea(),
|
||||||
|
required=False,
|
||||||
|
label=_('Welcome text'),
|
||||||
|
help_text=_('Printed in PDF of first time passwords only.')))
|
||||||
|
participant_sort_users_by_first_name = ConfigVariable(
|
||||||
|
name='participant_sort_users_by_first_name',
|
||||||
|
default_value=False,
|
||||||
|
form_field=forms.BooleanField(
|
||||||
|
required=False,
|
||||||
|
label=_('Sort participants by first name'),
|
||||||
|
help_text=_('Disable for sorting by last name')))
|
||||||
|
|
||||||
|
return ConfigPage(title=ugettext_noop('Participant'),
|
||||||
|
url='participant',
|
||||||
|
required_permission='config.can_manage',
|
||||||
|
weight=50,
|
||||||
|
variables=(participant_pdf_system_url,
|
||||||
|
participant_pdf_welcometext,
|
||||||
|
participant_sort_users_by_first_name))
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_database_setup, dispatch_uid='participant_create_builtin_groups')
|
@receiver(post_database_setup, dispatch_uid='participant_create_builtin_groups')
|
||||||
def create_builtin_groups(sender, **kwargs):
|
def create_builtin_groups(sender, **kwargs):
|
||||||
"""
|
"""
|
||||||
@ -69,7 +111,7 @@ def create_builtin_groups(sender, **kwargs):
|
|||||||
perm_15a = Permission.objects.get(content_type=ct_mediafile, codename='can_manage')
|
perm_15a = Permission.objects.get(content_type=ct_mediafile, codename='can_manage')
|
||||||
|
|
||||||
ct_config = ContentType.objects.get(app_label='config', model='configstore')
|
ct_config = ContentType.objects.get(app_label='config', model='configstore')
|
||||||
perm_16 = Permission.objects.get(content_type=ct_config, codename='can_manage_config')
|
perm_16 = Permission.objects.get(content_type=ct_config, codename='can_manage')
|
||||||
|
|
||||||
group_staff = Group.objects.create(name=ugettext_noop('Staff'))
|
group_staff = Group.objects.create(name=ugettext_noop('Staff'))
|
||||||
group_staff.permissions.add(perm_7, perm_9, perm_10, perm_11, perm_12, perm_13, perm_14, perm_15, perm_15a, perm_16)
|
group_staff.permissions.add(perm_7, perm_9, perm_10, perm_11, perm_12, perm_13, perm_14, perm_15, perm_15a, perm_16)
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
{% extends "config/base_config.html" %}
|
|
||||||
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block title %}{{ block.super }} – {% trans "Participant settings" %}{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<h1>
|
|
||||||
{% trans "Configuration" %}
|
|
||||||
<small>{% trans "Participants" %}</small>
|
|
||||||
{% block config_submenu %}{{ block.super }}{% endblock %}
|
|
||||||
</h1>
|
|
||||||
<form action="" method="post">{% csrf_token %}
|
|
||||||
{% include "form.html" %}
|
|
||||||
<p>
|
|
||||||
{% include "formbuttons_save.html" %}
|
|
||||||
<a href="{% url 'config_participant' %}" class="btn">
|
|
||||||
{% trans 'Cancel' %}
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
<small>* {% trans "required" %}</small>
|
|
||||||
</form>
|
|
||||||
{% endblock %}
|
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
URL list for the participant app.
|
URL list for the participant app.
|
||||||
|
|
||||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
Views for the participant app.
|
Views for the participant app.
|
||||||
|
|
||||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -41,14 +41,14 @@ from openslides.utils.utils import (
|
|||||||
from openslides.utils.views import (
|
from openslides.utils.views import (
|
||||||
FormView, PDFView, CreateView, UpdateView, DeleteView, PermissionMixin,
|
FormView, PDFView, CreateView, UpdateView, DeleteView, PermissionMixin,
|
||||||
RedirectView, SingleObjectMixin, ListView, QuestionMixin, DetailView)
|
RedirectView, SingleObjectMixin, ListView, QuestionMixin, DetailView)
|
||||||
from openslides.config.models import config
|
from openslides.config.api import config
|
||||||
from openslides.projector.projector import Widget
|
from openslides.projector.projector import Widget
|
||||||
from openslides.motion.models import Motion
|
from openslides.motion.models import Motion
|
||||||
from openslides.assignment.models import Assignment
|
from openslides.assignment.models import Assignment
|
||||||
from openslides.participant.api import gen_username, gen_password, import_users
|
from openslides.participant.api import gen_username, gen_password, import_users
|
||||||
from openslides.participant.forms import (
|
from openslides.participant.forms import (
|
||||||
UserCreateForm, UserUpdateForm, UsersettingsForm,
|
UserCreateForm, UserUpdateForm, UsersettingsForm,
|
||||||
UserImportForm, GroupForm, ConfigForm)
|
UserImportForm, GroupForm)
|
||||||
from openslides.participant.models import User, Group
|
from openslides.participant.models import User, Group
|
||||||
|
|
||||||
|
|
||||||
@ -388,34 +388,6 @@ class GroupDeleteView(DeleteView):
|
|||||||
super(GroupDeleteView, self).pre_redirect(request, *args, **kwargs)
|
super(GroupDeleteView, self).pre_redirect(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class Config(FormView):
|
|
||||||
"""
|
|
||||||
Config page for the participant app.
|
|
||||||
"""
|
|
||||||
permission_required = 'config.can_manage_config'
|
|
||||||
form_class = ConfigForm
|
|
||||||
template_name = 'participant/config.html'
|
|
||||||
success_url_name = 'config_participant'
|
|
||||||
|
|
||||||
def get_initial(self):
|
|
||||||
return {
|
|
||||||
'participant_pdf_system_url': config['participant_pdf_system_url'],
|
|
||||||
'participant_pdf_welcometext': config['participant_pdf_welcometext'],
|
|
||||||
'participant_sort_users_by_first_name': config['participant_sort_users_by_first_name']}
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
config['participant_pdf_system_url'] = (
|
|
||||||
form.cleaned_data['participant_pdf_system_url'])
|
|
||||||
config['participant_pdf_welcometext'] = (
|
|
||||||
form.cleaned_data['participant_pdf_welcometext'])
|
|
||||||
config['participant_sort_users_by_first_name'] = (
|
|
||||||
form.cleaned_data['participant_sort_users_by_first_name'])
|
|
||||||
messages.success(
|
|
||||||
self.request,
|
|
||||||
_('Participants settings successfully saved.'))
|
|
||||||
return super(Config, self).form_valid(form)
|
|
||||||
|
|
||||||
|
|
||||||
def login(request):
|
def login(request):
|
||||||
extra_content = {}
|
extra_content = {}
|
||||||
try:
|
try:
|
||||||
@ -485,7 +457,7 @@ def user_settings_password(request):
|
|||||||
|
|
||||||
def register_tab(request):
|
def register_tab(request):
|
||||||
"""
|
"""
|
||||||
Register the participant tab.
|
Registers the participant tab.
|
||||||
"""
|
"""
|
||||||
selected = request.path.startswith('/participant/')
|
selected = request.path.startswith('/participant/')
|
||||||
return Tab(
|
return Tab(
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
openslides.projector
|
||||||
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The projector app.
|
||||||
|
|
||||||
|
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||||
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from . import signals
|
@ -15,7 +15,7 @@ from django.core.cache import cache
|
|||||||
from django.utils.datastructures import SortedDict
|
from django.utils.datastructures import SortedDict
|
||||||
from django.utils.importlib import import_module
|
from django.utils.importlib import import_module
|
||||||
|
|
||||||
from openslides.config.models import config
|
from openslides.config.api import config
|
||||||
from openslides.projector.projector import SLIDE, Slide
|
from openslides.projector.projector import SLIDE, Slide
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
Models for the projector app.
|
Models for the projector app.
|
||||||
|
|
||||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -14,8 +14,6 @@ from django.db import models
|
|||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.utils.translation import ugettext_lazy as _, ugettext_noop
|
from django.utils.translation import ugettext_lazy as _, ugettext_noop
|
||||||
|
|
||||||
from openslides.config.signals import default_config_value
|
|
||||||
|
|
||||||
from openslides.projector.api import register_slidemodel
|
from openslides.projector.api import register_slidemodel
|
||||||
from openslides.projector.projector import SlideMixin
|
from openslides.projector.projector import SlideMixin
|
||||||
|
|
||||||
@ -70,16 +68,3 @@ class ProjectorOverlay(models.Model):
|
|||||||
if self.sid:
|
if self.sid:
|
||||||
return "%s on %s" % (self.def_name, self.sid)
|
return "%s on %s" % (self.def_name, self.sid)
|
||||||
return self.def_name
|
return self.def_name
|
||||||
|
|
||||||
|
|
||||||
@receiver(default_config_value, dispatch_uid="projector_default_config")
|
|
||||||
def default_config(sender, key, **kwargs):
|
|
||||||
return {
|
|
||||||
'projector_message': '',
|
|
||||||
'countdown_time': 60,
|
|
||||||
'countdown_start_stamp': 0,
|
|
||||||
'countdown_pause_stamp': 0,
|
|
||||||
'countdown_state': 'inactive',
|
|
||||||
'bigger': 100,
|
|
||||||
'up': 0,
|
|
||||||
}.get(key)
|
|
||||||
|
@ -15,10 +15,10 @@ from time import time
|
|||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
|
|
||||||
from openslides.config.models import config
|
from openslides.config.api import config
|
||||||
|
|
||||||
from openslides.projector.signals import projector_overlays
|
from openslides.projector.signals import projector_overlays
|
||||||
|
|
||||||
|
|
||||||
SLIDE = {}
|
SLIDE = {}
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,12 +4,68 @@
|
|||||||
openslides.projector.signals
|
openslides.projector.signals
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Defines Signals for the projector.
|
Signals for the projector app.
|
||||||
|
|
||||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.dispatch import Signal
|
from django.dispatch import Signal, receiver
|
||||||
|
from django import forms
|
||||||
|
from django.utils.translation import ugettext_lazy, ugettext as _
|
||||||
|
|
||||||
|
from openslides.config.signals import config_signal
|
||||||
|
from openslides.config.api import ConfigVariable, ConfigPage
|
||||||
|
|
||||||
|
|
||||||
projector_overlays = Signal(providing_args=['register', 'call'])
|
projector_overlays = Signal(providing_args=['register', 'call'])
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(config_signal, dispatch_uid='setup_projector_config_variables')
|
||||||
|
def setup_projector_config_variables(sender, **kwargs):
|
||||||
|
"""
|
||||||
|
Projector config variables for OpenSlides. They are not shown on a
|
||||||
|
config page.
|
||||||
|
"""
|
||||||
|
|
||||||
|
presentation = ConfigVariable(
|
||||||
|
name='presentation',
|
||||||
|
default_value='')
|
||||||
|
|
||||||
|
presentation_argument = ConfigVariable(
|
||||||
|
name='presentation_argument',
|
||||||
|
default_value=None)
|
||||||
|
|
||||||
|
projector_message = ConfigVariable(
|
||||||
|
name='projector_message',
|
||||||
|
default_value='')
|
||||||
|
|
||||||
|
countdown_time = ConfigVariable(
|
||||||
|
name='countdown_time',
|
||||||
|
default_value=60)
|
||||||
|
|
||||||
|
countdown_start_stamp = ConfigVariable(
|
||||||
|
name='countdown_start_stamp',
|
||||||
|
default_value=0)
|
||||||
|
|
||||||
|
countdown_pause_stamp = ConfigVariable(
|
||||||
|
name='countdown_pause_stamp',
|
||||||
|
default_value=0)
|
||||||
|
|
||||||
|
countdown_state = ConfigVariable(
|
||||||
|
name='countdown_state',
|
||||||
|
default_value='inactive')
|
||||||
|
|
||||||
|
bigger = ConfigVariable(
|
||||||
|
name='bigger',
|
||||||
|
default_value=100)
|
||||||
|
|
||||||
|
up = ConfigVariable(
|
||||||
|
name='up',
|
||||||
|
default_value=0)
|
||||||
|
|
||||||
|
return ConfigPage(title='No title here',
|
||||||
|
url='bar',
|
||||||
|
required_permission=None,
|
||||||
|
variables=(presentation, presentation_argument, projector_message, countdown_time,
|
||||||
|
countdown_start_stamp, countdown_pause_stamp, countdown_state, bigger, up))
|
||||||
|
@ -26,7 +26,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
from openslides.utils.template import render_block_to_string, Tab
|
from openslides.utils.template import render_block_to_string, Tab
|
||||||
from openslides.utils.views import (
|
from openslides.utils.views import (
|
||||||
TemplateView, RedirectView, CreateView, UpdateView, DeleteView, AjaxMixin)
|
TemplateView, RedirectView, CreateView, UpdateView, DeleteView, AjaxMixin)
|
||||||
from openslides.config.models import config
|
from openslides.config.api import config
|
||||||
from .api import (
|
from .api import (
|
||||||
get_active_slide, set_active_slide, projector_message_set,
|
get_active_slide, set_active_slide, projector_message_set,
|
||||||
projector_message_delete, get_slide_from_sid, get_all_widgets,
|
projector_message_delete, get_slide_from_sid, get_all_widgets,
|
||||||
@ -224,8 +224,8 @@ class ProjectorEdit(RedirectView):
|
|||||||
if config['up'] < 0:
|
if config['up'] < 0:
|
||||||
config['up'] = int(config['up']) + 10
|
config['up'] = int(config['up']) + 10
|
||||||
elif direction == 'clean':
|
elif direction == 'clean':
|
||||||
config['up'] = 0
|
config['up'] = 0 # TODO: Use default value from the signal instead of fix value here
|
||||||
config['bigger'] = 100
|
config['bigger'] = 100 # TODO: Use default value from the signal instead of fix value here
|
||||||
|
|
||||||
|
|
||||||
class CountdownEdit(RedirectView):
|
class CountdownEdit(RedirectView):
|
||||||
|
@ -98,7 +98,7 @@
|
|||||||
<hr />
|
<hr />
|
||||||
<footer>
|
<footer>
|
||||||
<small>
|
<small>
|
||||||
© Copyright 2011-2013 | Powered by <a href="http://openslides.org" target="_blank">OpenSlides</a>
|
© Copyright 2011–2013 | Powered by <a href="http://openslides.org" target="_blank">OpenSlides</a>
|
||||||
</small>
|
</small>
|
||||||
</footer>
|
</footer>
|
||||||
</div><!--/content-->
|
</div><!--/content-->
|
||||||
@ -112,6 +112,9 @@
|
|||||||
<script src="{% static 'javascript/bootstrap.min.js' %}" type="text/javascript"></script>
|
<script src="{% static 'javascript/bootstrap.min.js' %}" type="text/javascript"></script>
|
||||||
<script src="{% static 'javascript/utils.js' %}" type="text/javascript"></script>
|
<script src="{% static 'javascript/utils.js' %}" type="text/javascript"></script>
|
||||||
<script src="{% url 'django.views.i18n.javascript_catalog' %}" type="text/javascript"></script>
|
<script src="{% url 'django.views.i18n.javascript_catalog' %}" type="text/javascript"></script>
|
||||||
|
{% for javascript in extra_javascript %}
|
||||||
|
<script src="{% static javascript %}" type="text/javascript"></script>
|
||||||
|
{% endfor %}
|
||||||
{% block javascript %}{% endblock %}
|
{% block javascript %}{% endblock %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -14,15 +14,10 @@ from django.conf import settings
|
|||||||
from django.conf.urls import patterns, url, include
|
from django.conf.urls import patterns, url, include
|
||||||
from django.utils.importlib import import_module
|
from django.utils.importlib import import_module
|
||||||
|
|
||||||
from openslides.utils.views import RedirectView
|
|
||||||
|
|
||||||
|
|
||||||
handler500 = 'openslides.utils.views.server_error'
|
handler500 = 'openslides.utils.views.server_error'
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
# Redirect to dashboard URL
|
|
||||||
url(r'^$', RedirectView.as_view(url='projector/dashboard'), name='home',),
|
|
||||||
|
|
||||||
(r'^agenda/', include('openslides.agenda.urls')),
|
(r'^agenda/', include('openslides.agenda.urls')),
|
||||||
(r'^motion/', include('openslides.motion.urls')),
|
(r'^motion/', include('openslides.motion.urls')),
|
||||||
(r'^assignment/', include('openslides.assignment.urls')),
|
(r'^assignment/', include('openslides.assignment.urls')),
|
||||||
@ -69,3 +64,7 @@ urlpatterns += patterns('',
|
|||||||
name='password_change',
|
name='password_change',
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
urlpatterns += patterns('',
|
||||||
|
(r'^', include('openslides.core.urls')),
|
||||||
|
)
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from django.contrib.auth.models import Permission
|
from django.contrib.auth.models import Permission
|
||||||
from openslides.config.models import config
|
from openslides.config.api import config
|
||||||
|
|
||||||
|
|
||||||
class AnonymousAuth(object):
|
class AnonymousAuth(object):
|
||||||
|
@ -23,7 +23,7 @@ from django.conf import settings
|
|||||||
from django.utils import formats
|
from django.utils import formats
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from openslides.config.models import config
|
from openslides.config.api import config
|
||||||
|
|
||||||
|
|
||||||
# register new truetype fonts
|
# register new truetype fonts
|
||||||
|
@ -11,7 +11,8 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from django import template
|
from django import template
|
||||||
from openslides.config.models import config
|
from openslides.config.api import config
|
||||||
|
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
|
@ -14,14 +14,16 @@
|
|||||||
from django.test import TestCase as _TestCase
|
from django.test import TestCase as _TestCase
|
||||||
|
|
||||||
from openslides.core.signals import post_database_setup
|
from openslides.core.signals import post_database_setup
|
||||||
|
from openslides.config.api import config
|
||||||
|
|
||||||
|
|
||||||
class TestCase(_TestCase):
|
class TestCase(_TestCase):
|
||||||
"""
|
"""
|
||||||
Overwrites Django's TestCase class to call the post_database_setup
|
Overwrites Django's TestCase class to call the post_database_setup
|
||||||
signal after the preparation of every test.
|
signal after the preparation of every test. Also refreshs the config cache.
|
||||||
"""
|
"""
|
||||||
def _pre_setup(self, *args, **kwargs):
|
def _pre_setup(self, *args, **kwargs):
|
||||||
return_value = super(TestCase, self)._pre_setup(*args, **kwargs)
|
return_value = super(TestCase, self)._pre_setup(*args, **kwargs)
|
||||||
post_database_setup.send(sender=self)
|
post_database_setup.send(sender=self)
|
||||||
|
config.setup_cache()
|
||||||
return return_value
|
return return_value
|
||||||
|
@ -391,7 +391,10 @@ def send_register_tab(sender, request, context, **kwargs):
|
|||||||
Inserts the tab objects and also the extra_stylefiles to the context.
|
Inserts the tab objects and also the extra_stylefiles to the context.
|
||||||
"""
|
"""
|
||||||
tabs = []
|
tabs = []
|
||||||
extra_stylefiles = []
|
if 'extra_stylefiles' in context:
|
||||||
|
extra_stylefiles = context['extra_stylefiles']
|
||||||
|
else:
|
||||||
|
extra_stylefiles = []
|
||||||
for app in settings.INSTALLED_APPS:
|
for app in settings.INSTALLED_APPS:
|
||||||
try:
|
try:
|
||||||
mod = import_module(app + '.views')
|
mod = import_module(app + '.views')
|
||||||
@ -401,8 +404,6 @@ def send_register_tab(sender, request, context, **kwargs):
|
|||||||
extra_stylefiles.append(tab.stylefile)
|
extra_stylefiles.append(tab.stylefile)
|
||||||
except (ImportError, AttributeError):
|
except (ImportError, AttributeError):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
context.update({
|
context.update({
|
||||||
'tabs': tabs,
|
'tabs': tabs,
|
||||||
'extra_stylefiles': extra_stylefiles,
|
'extra_stylefiles': extra_stylefiles})
|
||||||
})
|
|
||||||
|
@ -191,3 +191,18 @@ class ViewTest(TestCase):
|
|||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.refreshItems()
|
self.refreshItems()
|
||||||
self.assertEqual(self.item1.title, 'newitem1')
|
self.assertEqual(self.item1.title, 'newitem1')
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigTest(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.admin = User.objects.create(username='config_test_admin')
|
||||||
|
self.admin.reset_password('default')
|
||||||
|
self.admin.is_superuser = True
|
||||||
|
self.admin.save()
|
||||||
|
self.client = Client()
|
||||||
|
self.client.login(username='config_test_admin', password='default')
|
||||||
|
|
||||||
|
def test_config_page_css_javascript(self):
|
||||||
|
response = self.client.get('/config/agenda/')
|
||||||
|
self.assertContains(response, 'timepicker.css', status_code=200)
|
||||||
|
self.assertContains(response, 'jquery-ui-timepicker-addon.min.js', status_code=200)
|
||||||
|
0
tests/config/__init__.py
Normal file
0
tests/config/__init__.py
Normal file
259
tests/config/test_config.py
Normal file
259
tests/config/test_config.py
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Tests for openslides.config
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||||
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.test.client import Client
|
||||||
|
from django.dispatch import receiver
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.contrib.auth.models import Permission
|
||||||
|
from django import forms
|
||||||
|
|
||||||
|
from openslides.utils.test import TestCase
|
||||||
|
from openslides.participant.models import User
|
||||||
|
from openslides.config.api import config, ConfigGroupedPage, ConfigPage, ConfigGroup, ConfigVariable
|
||||||
|
from openslides.config.signals import config_signal
|
||||||
|
from openslides.config.exceptions import ConfigError, ConfigNotFound
|
||||||
|
|
||||||
|
|
||||||
|
class HandleConfigTest(TestCase):
|
||||||
|
|
||||||
|
def get_config_var(self, key):
|
||||||
|
return config[key]
|
||||||
|
|
||||||
|
def test_get_config_default_value(self):
|
||||||
|
self.assertEqual(config['string_var'], 'default_string_rien4ooCZieng6ah')
|
||||||
|
self.assertTrue(config['bool_var'])
|
||||||
|
self.assertEqual(config['integer_var'], 3)
|
||||||
|
self.assertEqual(config['choices_var'], 1)
|
||||||
|
self.assertEqual(config['none_config_var'], None)
|
||||||
|
self.assertRaisesMessage(expected_exception=ConfigNotFound,
|
||||||
|
expected_message='The config variable unknown_config_var was not found.',
|
||||||
|
callable_obj=self.get_config_var, key='unknown_config_var')
|
||||||
|
|
||||||
|
def test_get_multiple_config_var_error(self):
|
||||||
|
config_signal.connect(set_simple_config_page_multiple_vars, dispatch_uid='set_simple_config_page_multiple_vars_for_testing')
|
||||||
|
self.assertRaisesMessage(expected_exception=ConfigError,
|
||||||
|
expected_message='Too many values for config variable multiple_config_var found.',
|
||||||
|
callable_obj=config.setup_cache)
|
||||||
|
config_signal.disconnect(set_simple_config_page_multiple_vars, dispatch_uid='set_simple_config_page_multiple_vars_for_testing')
|
||||||
|
|
||||||
|
def test_database_queries(self):
|
||||||
|
self.assertNumQueries(0, self.get_config_var, key='string_var')
|
||||||
|
|
||||||
|
def test_setup_config_var(self):
|
||||||
|
self.assertRaises(TypeError, ConfigVariable)
|
||||||
|
self.assertRaises(TypeError, ConfigVariable, name='foo')
|
||||||
|
self.assertRaises(TypeError, ConfigVariable, default_value='foo')
|
||||||
|
|
||||||
|
def test_change_config_value(self):
|
||||||
|
self.assertEqual(config['string_var'], 'default_string_rien4ooCZieng6ah')
|
||||||
|
config['string_var'] = 'other_special_unique_string dauTex9eAiy7jeen'
|
||||||
|
self.assertEqual(config['string_var'], 'other_special_unique_string dauTex9eAiy7jeen')
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigFormTest(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
# Setup the permission
|
||||||
|
ct = ContentType.objects.get(app_label='config', model='configstore')
|
||||||
|
perm = Permission.objects.get(content_type=ct, codename='can_manage')
|
||||||
|
|
||||||
|
# Setup two users
|
||||||
|
self.manager = User.objects.create(username='config_test_manager')
|
||||||
|
self.manager.reset_password('default')
|
||||||
|
self.manager.user_permissions.add(perm)
|
||||||
|
|
||||||
|
self.normal_user = User.objects.create(username='config_test_normal_user')
|
||||||
|
self.normal_user.reset_password('default')
|
||||||
|
|
||||||
|
# Login
|
||||||
|
self.client_manager = Client()
|
||||||
|
self.client_manager.login(username='config_test_manager', password='default')
|
||||||
|
self.client_normal_user = Client()
|
||||||
|
self.client_normal_user.login(username='config_test_normal_user', password='default')
|
||||||
|
|
||||||
|
def test_get_config_form_overview(self):
|
||||||
|
response = self.client_manager.get('/config/')
|
||||||
|
self.assertRedirects(response=response, expected_url='/config/general/',
|
||||||
|
status_code=302, target_status_code=200)
|
||||||
|
bad_client = Client()
|
||||||
|
response = bad_client.get('/config/', follow=True)
|
||||||
|
self.assertRedirects(response=response, expected_url='/login/?next=/config/general/',
|
||||||
|
status_code=302, target_status_code=200)
|
||||||
|
|
||||||
|
def test_get_config_form_testgroupedpage1_manager_client(self):
|
||||||
|
response = self.client_manager.get('/config/testgroupedpage1/')
|
||||||
|
self.assertContains(response=response, text='default_string_rien4ooCZieng6ah', status_code=200)
|
||||||
|
self.assertTemplateUsed(response=response, template_name='base.html')
|
||||||
|
self.assertTemplateUsed(response=response, template_name='config/config_form.html')
|
||||||
|
self.assertTemplateNotUsed(response=response, template_name='form.html')
|
||||||
|
self.assertTemplateUsed(response=response, template_name='formbuttons_save.html')
|
||||||
|
|
||||||
|
def test_get_config_form_testgroupedpage1_grouping(self):
|
||||||
|
response = self.client_manager.get('/config/testgroupedpage1/')
|
||||||
|
self.assertContains(response=response, text='Group 1 aiYeix2mCieQuae3', status_code=200)
|
||||||
|
self.assertContains(response=response, text='Group 2 Toongai7ahyahy7B', status_code=200)
|
||||||
|
|
||||||
|
def test_get_config_form_testgroupedpage1_other_clients(self):
|
||||||
|
response = self.client_normal_user.get('/config/testgroupedpage1/')
|
||||||
|
self.assertEqual(response.status_code, 403)
|
||||||
|
bad_client = Client()
|
||||||
|
response = bad_client.get('/config/testgroupedpage1/')
|
||||||
|
self.assertRedirects(response=response, expected_url='/login/?next=/config/testgroupedpage1/',
|
||||||
|
status_code=302, target_status_code=200)
|
||||||
|
|
||||||
|
def test_get_config_form_testsimplepage1_other_clients(self):
|
||||||
|
response = self.client_normal_user.get('/config/testsimplepage1/')
|
||||||
|
self.assertNotContains(response=response, text='BaeB0ahcMae3feem', status_code=200)
|
||||||
|
self.assertTemplateUsed(response=response, template_name='base.html')
|
||||||
|
self.assertTemplateUsed(response=response, template_name='config/config_form.html')
|
||||||
|
self.assertTemplateUsed(response=response, template_name='form.html')
|
||||||
|
self.assertTemplateUsed(response=response, template_name='formbuttons_save.html')
|
||||||
|
bad_client = Client()
|
||||||
|
response = bad_client.get('/config/testsimplepage1/')
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertNotContains(response=response, text='BaeB0ahcMae3feem', status_code=200)
|
||||||
|
|
||||||
|
def test_get_config_form_testgroupedpage1_initial(self):
|
||||||
|
config['string_var'] = 'something unique AChie6eeiDie3Ieciy1bah4I'
|
||||||
|
response = self.client_manager.get('/config/testgroupedpage1/')
|
||||||
|
self.assertContains(response=response, text='AChie6eeiDie3Ieciy1bah4I', status_code=200)
|
||||||
|
|
||||||
|
def test_get_config_form_testgroupedpage1_choices(self):
|
||||||
|
response = self.client_manager.get('/config/testgroupedpage1/')
|
||||||
|
self.assertContains(response=response, text='Ughoch4ocoche6Ee', status_code=200)
|
||||||
|
self.assertContains(response=response, text='Vahnoh5yalohv5Eb', status_code=200)
|
||||||
|
|
||||||
|
def test_post_config_form_configtest1(self):
|
||||||
|
response = self.client_manager.post(
|
||||||
|
'/config/testgroupedpage1/',
|
||||||
|
{'string_var': 'other_special_unique_string faiPaid4utie6eeL',
|
||||||
|
'integer_var': 3,
|
||||||
|
'choices_var': 2})
|
||||||
|
self.assertRedirects(response=response, expected_url='/config/testgroupedpage1/',
|
||||||
|
status_code=302, target_status_code=200)
|
||||||
|
self.assertEqual(config['string_var'], 'other_special_unique_string faiPaid4utie6eeL')
|
||||||
|
self.assertFalse(config['bool_var'])
|
||||||
|
self.assertEqual(config['integer_var'], 3)
|
||||||
|
self.assertEqual(config['choices_var'], 2)
|
||||||
|
|
||||||
|
def test_post_config_form_error(self):
|
||||||
|
response = self.client_manager.post(
|
||||||
|
'/config/testgroupedpage1/',
|
||||||
|
{'integer_var': 'bad_string_value'})
|
||||||
|
self.assertContains(response=response, text='errorlist', status_code=200)
|
||||||
|
|
||||||
|
def test_disabled_config_page(self):
|
||||||
|
response = self.client_manager.get('/config/testsimplepage3/')
|
||||||
|
self.assertEqual(response.status_code, 404)
|
||||||
|
response = self.client_manager.get('/config/testgroupedpage1/')
|
||||||
|
self.assertNotContains(response=response, text='Ho5iengaoon5Hoht', status_code=200)
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigWeightTest(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
# Setup the permission
|
||||||
|
ct = ContentType.objects.get(app_label='config', model='configstore')
|
||||||
|
perm = Permission.objects.get(content_type=ct, codename='can_manage')
|
||||||
|
|
||||||
|
# Setup two users
|
||||||
|
self.manager = User.objects.create(username='config_test_manager')
|
||||||
|
self.manager.reset_password('default')
|
||||||
|
self.manager.user_permissions.add(perm)
|
||||||
|
|
||||||
|
# Login
|
||||||
|
self.client_manager = Client()
|
||||||
|
self.client_manager.login(username='config_test_manager', password='default')
|
||||||
|
|
||||||
|
def test_order_of_config_pages_abstract(self):
|
||||||
|
config_page_dict = {}
|
||||||
|
for receiver, config_page in config_signal.send(sender=self):
|
||||||
|
config_page_dict[receiver.__name__] = config_page
|
||||||
|
self.assertGreater(config_page_dict['set_grouped_config_page'].weight, config_page_dict['set_simple_config_page'].weight)
|
||||||
|
|
||||||
|
def test_order_of_config_pages_on_view(self):
|
||||||
|
response = self.client_manager.get('/config/testgroupedpage1/')
|
||||||
|
import re
|
||||||
|
m1 = re.search('<a href="/config/testgroupedpage1/" class="btn btn-mini active">Config vars for testing 1</a>', response.content)
|
||||||
|
m2 = re.search('<a href="/config/testsimplepage1/" class="btn btn-mini ">Config vars for testing 2</a>', response.content)
|
||||||
|
self.assertGreater(m1.start(), m2.start())
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(config_signal, dispatch_uid='set_grouped_config_page_for_testing')
|
||||||
|
def set_grouped_config_page(sender, **kwargs):
|
||||||
|
"""
|
||||||
|
Sets a grouped config page which can be reached under the url
|
||||||
|
'/config/testgroupedpage1/'. There are some variables, one variable
|
||||||
|
with a string as default value, one with a boolean as default value,
|
||||||
|
one with an integer as default value, one with choices and one
|
||||||
|
hidden variable. These variables are grouped in two groups.
|
||||||
|
"""
|
||||||
|
string_var = ConfigVariable(
|
||||||
|
name='string_var',
|
||||||
|
default_value='default_string_rien4ooCZieng6ah',
|
||||||
|
form_field=forms.CharField())
|
||||||
|
bool_var = ConfigVariable(
|
||||||
|
name='bool_var',
|
||||||
|
default_value=True,
|
||||||
|
form_field=forms.BooleanField(required=False))
|
||||||
|
integer_var = ConfigVariable(
|
||||||
|
name='integer_var',
|
||||||
|
default_value=3,
|
||||||
|
form_field=forms.IntegerField())
|
||||||
|
group_1 = ConfigGroup(title='Group 1 aiYeix2mCieQuae3', variables=(string_var, bool_var, integer_var))
|
||||||
|
|
||||||
|
hidden_var = ConfigVariable(
|
||||||
|
name='hidden_var',
|
||||||
|
default_value='hidden_value')
|
||||||
|
choices_var = ConfigVariable(
|
||||||
|
name='choices_var',
|
||||||
|
default_value=1,
|
||||||
|
form_field=forms.ChoiceField(choices=((1, 'Choice One Ughoch4ocoche6Ee'), (2, 'Choice Two Vahnoh5yalohv5Eb'))))
|
||||||
|
group_2 = ConfigGroup(title='Group 2 Toongai7ahyahy7B', variables=(hidden_var, choices_var))
|
||||||
|
|
||||||
|
return ConfigGroupedPage(title='Config vars for testing 1',
|
||||||
|
url='testgroupedpage1',
|
||||||
|
required_permission='config.can_manage',
|
||||||
|
weight=10000,
|
||||||
|
groups=(group_1, group_2))
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(config_signal, dispatch_uid='set_simple_config_page_for_testing')
|
||||||
|
def set_simple_config_page(sender, **kwargs):
|
||||||
|
"""
|
||||||
|
Sets a simple config page with some config variables but without
|
||||||
|
grouping.
|
||||||
|
"""
|
||||||
|
return ConfigPage(title='Config vars for testing 2',
|
||||||
|
url='testsimplepage1',
|
||||||
|
required_permission='No permission required',
|
||||||
|
variables=(ConfigVariable(name='additional_config_var', default_value='BaeB0ahcMae3feem'),
|
||||||
|
ConfigVariable(name='additional_config_var_2', default_value='', form_field=forms.CharField()),
|
||||||
|
ConfigVariable(name='none_config_var', default_value=None)))
|
||||||
|
|
||||||
|
|
||||||
|
# Do not connect to the signal now but later inside the test.
|
||||||
|
def set_simple_config_page_multiple_vars(sender, **kwargs):
|
||||||
|
"""
|
||||||
|
Sets a bad config page with some multiple config vars.
|
||||||
|
"""
|
||||||
|
return ConfigPage(title='Config vars for testing 3',
|
||||||
|
url='testsimplepage2',
|
||||||
|
required_permission='No permission required',
|
||||||
|
variables=(ConfigVariable(name='multiple_config_var', default_value='foobar1'),
|
||||||
|
ConfigVariable(name='multiple_config_var', default_value='foobar2')))
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(config_signal, dispatch_uid='set_simple_config_page_disabled_page_for_testing')
|
||||||
|
def set_simple_config_page_disabled_page(sender, **kwargs):
|
||||||
|
return ConfigPage(title='Ho5iengaoon5Hoht',
|
||||||
|
url='testsimplepage3',
|
||||||
|
required_permission='No permission required',
|
||||||
|
variables=(ConfigVariable(name='hidden_config_var_2', default_value=''),))
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
from openslides.utils.test import TestCase
|
from openslides.utils.test import TestCase
|
||||||
from openslides.participant.models import User
|
from openslides.participant.models import User
|
||||||
from openslides.config.models import config
|
from openslides.config.api import config
|
||||||
from openslides.motion.models import Motion, Workflow, State
|
from openslides.motion.models import Motion, Workflow, State
|
||||||
from openslides.motion.exceptions import WorkflowError
|
from openslides.motion.exceptions import WorkflowError
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user