Merge pull request #573 from normanjaeckel/ConfigRebase1
New config app. Apps only have to define config vars once. Config pages,...
This commit is contained in:
commit
c0532def79
@ -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 %}
|
||||||
|
<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>
|
||||||
|
{% endfor %}
|
||||||
|
<a href="{% url 'core_version' %}" class="btn btn-mini">{% trans 'Version' %}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</small>
|
||||||
|
</h1>
|
||||||
|
<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>
|
<p>
|
||||||
{% include "formbuttons_save.html" %}
|
{% include 'formbuttons_save.html' %}
|
||||||
<a href="{% url 'config_agenda' %}" class="btn">
|
<a href="/config/{{ active_config_page.url }}/" class="btn">{% trans 'Cancel' %}</a>
|
||||||
{% trans 'Cancel' %}
|
|
||||||
</a>
|
|
||||||
</p>
|
</p>
|
||||||
<small>* {% trans "required" %}</small>
|
<small>* {% trans 'required' %}</small>
|
||||||
</form>
|
</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'],
|
|
||||||
'event_description': config['event_description'],
|
|
||||||
'event_date': config['event_date'],
|
|
||||||
'event_location': config['event_location'],
|
|
||||||
'event_organizer': config['event_organizer'],
|
|
||||||
'welcome_title': config['welcome_title'],
|
|
||||||
'welcome_text': config['welcome_text'],
|
|
||||||
'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.
|
Returns a dictonary with the actual values of the config variables
|
||||||
|
as intial value for the form.
|
||||||
"""
|
"""
|
||||||
permission_required = 'config.can_manage_config'
|
initial = super(ConfigView, self).get_initial()
|
||||||
template_name = 'config/version.html'
|
for variable in self.config_page.variables:
|
||||||
|
initial.update({variable.name: config[variable.name]})
|
||||||
|
return initial
|
||||||
|
|
||||||
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,6 +391,9 @@ 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 = []
|
||||||
|
if 'extra_stylefiles' in context:
|
||||||
|
extra_stylefiles = context['extra_stylefiles']
|
||||||
|
else:
|
||||||
extra_stylefiles = []
|
extra_stylefiles = []
|
||||||
for app in settings.INSTALLED_APPS:
|
for app in settings.INSTALLED_APPS:
|
||||||
try:
|
try:
|
||||||
@ -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