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.
|
||||
"""
|
||||
|
||||
import openslides.agenda.signals
|
||||
from . import signals
|
||||
|
@ -13,7 +13,7 @@
|
||||
import re
|
||||
|
||||
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 openslides.utils.forms import CssClassMixin
|
||||
@ -25,14 +25,14 @@ class ItemForm(forms.ModelForm, CssClassMixin):
|
||||
Form to create of update an item.
|
||||
"""
|
||||
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(
|
||||
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,
|
||||
required=False,
|
||||
label=_("Duration (hh:mm)"))
|
||||
label=ugettext_lazy("Duration (hh:mm)"))
|
||||
|
||||
class Meta:
|
||||
model = Item
|
||||
@ -46,7 +46,7 @@ def gen_weight_choices():
|
||||
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.
|
||||
"""
|
||||
@ -57,10 +57,3 @@ class ItemOrderForm(forms.Form, CssClassMixin):
|
||||
widget=forms.HiddenInput(attrs={'class': 'menu-mlid'}))
|
||||
parent = forms.IntegerField(
|
||||
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 openslides.config.models import config
|
||||
from openslides.config.api import config
|
||||
from openslides.projector.projector import SlideMixin
|
||||
from openslides.projector.api import (
|
||||
register_slidemodel, get_slide_from_sid, register_slidefunc)
|
||||
|
@ -6,17 +6,44 @@
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
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")
|
||||
def default_config(sender, key, **kwargs):
|
||||
"""Return the default config values for the agenda app."""
|
||||
return {
|
||||
'agenda_start_event_date_time': ''}.get(key)
|
||||
# TODO: Reinsert the datepicker scripts in the template
|
||||
|
||||
@receiver(config_signal, dispatch_uid='setup_agenda_config_page')
|
||||
def setup_agenda_config_page(sender, **kwargs):
|
||||
"""
|
||||
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.views.generic.detail import SingleObjectMixin
|
||||
|
||||
from openslides.config.models import config
|
||||
from openslides.agenda.forms import ConfigForm
|
||||
from openslides.config.api import config
|
||||
from openslides.utils.pdf import stylesheet
|
||||
from openslides.utils.views import (
|
||||
TemplateView, RedirectView, UpdateView, CreateView, DeleteView, PDFView,
|
||||
@ -225,29 +224,9 @@ class AgendaPDF(PDFView):
|
||||
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):
|
||||
"""
|
||||
register the agenda tab.
|
||||
Registers the agenda tab.
|
||||
"""
|
||||
selected = request.path.startswith('/agenda/')
|
||||
return Tab(
|
||||
@ -261,7 +240,7 @@ def register_tab(request):
|
||||
|
||||
def get_widgets(request):
|
||||
"""
|
||||
return the agenda widget for the projector-tab.
|
||||
Returns the agenda widget for the projector tab.
|
||||
"""
|
||||
return [Widget(
|
||||
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.
|
||||
|
||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
||||
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||
:license: GNU GPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
@ -33,39 +33,3 @@ class AssignmentRunForm(forms.Form, CssClassMixin):
|
||||
widget=forms.Select(attrs={'class': 'medium-input'}),
|
||||
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.
|
||||
|
||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
||||
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||
:license: GNU GPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db import models
|
||||
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 PersonField
|
||||
from openslides.config.models import config
|
||||
from openslides.config.signals import default_config_value
|
||||
from openslides.config.api import config
|
||||
from openslides.projector.api import register_slidemodel
|
||||
from openslides.projector.projector import SlideMixin
|
||||
from openslides.poll.models import (
|
||||
@ -308,15 +306,3 @@ class AssignmentPoll(BasePoll, CountInvalid, CountVotesCast, PublishPollMixin):
|
||||
|
||||
def __unicode__(self):
|
||||
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.
|
||||
|
||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
||||
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||
:license: GNU GPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
@ -30,14 +30,13 @@ from openslides.utils.utils import (
|
||||
template, permission_required, gen_confirm_form, del_confirm_form, ajax_request)
|
||||
from openslides.utils.views import FormView, DeleteView, PDFView, RedirectView
|
||||
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.projector.projector import Widget
|
||||
from openslides.poll.views import PollFormView
|
||||
from openslides.agenda.models import Item
|
||||
from openslides.assignment.models import Assignment, AssignmentPoll
|
||||
from openslides.assignment.forms import (
|
||||
AssignmentForm, AssignmentRunForm, ConfigForm)
|
||||
from openslides.assignment.forms import AssignmentForm, AssignmentRunForm
|
||||
|
||||
|
||||
@permission_required('assignment.can_see_assignment')
|
||||
@ -633,45 +632,6 @@ class AssignmentPollPDF(PDFView):
|
||||
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):
|
||||
selected = request.path.startswith('/assignment/')
|
||||
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.
|
||||
|
||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
||||
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||
: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.dispatch import receiver
|
||||
from django.utils.importlib import import_module
|
||||
from django.utils.translation import ugettext_lazy as _, ugettext_noop
|
||||
from django.utils.translation import ugettext_noop
|
||||
|
||||
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):
|
||||
"""
|
||||
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):
|
||||
return self.key
|
||||
key = models.CharField(max_length=255, primary_key=True)
|
||||
"""A string, the key of the config variable."""
|
||||
|
||||
value = JSONField()
|
||||
"""The value of the config variable. """
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'config'
|
||||
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})
|
||||
permissions = (('can_manage', ugettext_noop('Can manage configuration')),)
|
||||
|
@ -4,12 +4,14 @@
|
||||
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.
|
||||
"""
|
||||
|
||||
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 %}
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
{% 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 %}
|
||||
<script type="text/javascript" src="{% static 'javascript/jquery-ui.custom.min.js' %}"></script>
|
||||
<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>
|
||||
{{ block.super }}
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
$.datepicker.regional['{{ LANGUAGE_CODE }}'] = {
|
||||
@ -63,23 +56,47 @@
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}{{ block.super }} – {% trans "Agenda settings" %}{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<h1>
|
||||
{% trans "Configuration" %}
|
||||
<small>{% trans "Agenda" %}</small>
|
||||
{% block config_submenu %}{{ block.super }}{% endblock %}
|
||||
{% trans 'Configuration' %}
|
||||
<small>{% trans active_config_page.title %}</small>
|
||||
<small class="pull-right">
|
||||
<div class="btn-toolbar">
|
||||
<div class="btn-group">
|
||||
{% 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 %}
|
||||
{% include "form.html" %}
|
||||
{% for group in groups %}
|
||||
<fieldset>
|
||||
<legend>{{ group.title }}</legend>
|
||||
{% for field in form %}
|
||||
{% if field.name in group.get_field_names %}
|
||||
<div class="control-group {% if field.errors %}error{% endif%}">
|
||||
<label for="id_{{ field.name }}">{{ field.label }}{% if field.field.required %}<span class="required">*</span>{% endif %}:</label>
|
||||
{{ field }}
|
||||
{% if field.errors %}
|
||||
<span class="help-inline">{{ field.errors }}</span>
|
||||
{% endif %}
|
||||
{% if field.help_text %}
|
||||
<span class="help-inline">{{ field.help_text }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</fieldset>
|
||||
{% empty %}
|
||||
{% include 'form.html' %}
|
||||
{% endfor %}
|
||||
<p>
|
||||
{% include "formbuttons_save.html" %}
|
||||
<a href="{% url 'config_agenda' %}" class="btn">
|
||||
{% trans 'Cancel' %}
|
||||
</a>
|
||||
{% include 'formbuttons_save.html' %}
|
||||
<a href="/config/{{ active_config_page.url }}/" class="btn">{% trans 'Cancel' %}</a>
|
||||
</p>
|
||||
<small>* {% trans "required" %}</small>
|
||||
<small>* {% trans 'required' %}</small>
|
||||
</form>
|
||||
{% 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
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
from django.conf import settings
|
||||
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('',
|
||||
url(r'^general/$',
|
||||
GeneralConfig.as_view(),
|
||||
name='config_general',
|
||||
),
|
||||
|
||||
url(r'^version/$',
|
||||
VersionConfig.as_view(),
|
||||
name='config_version',
|
||||
url(r'^$',
|
||||
RedirectView.as_view(url_name='config_general'),
|
||||
name='config_first_config_page',
|
||||
),
|
||||
)
|
||||
|
||||
for app in settings.INSTALLED_APPS:
|
||||
try:
|
||||
mod = import_module(app + '.views')
|
||||
except ImportError:
|
||||
continue
|
||||
appname = mod.__name__.split('.')[-2]
|
||||
try:
|
||||
urlpatterns += patterns('', url(
|
||||
r'^%s/$' % appname,
|
||||
mod.Config.as_view(),
|
||||
name='config_%s' % appname,
|
||||
))
|
||||
except AttributeError:
|
||||
continue
|
||||
|
||||
for receiver, config_page in config_signal.send(sender='config_urls'):
|
||||
if config_page.is_shown():
|
||||
urlpatterns += patterns('', url(r'^%s/$' % config_page.url,
|
||||
ConfigView.as_view(config_page=config_page),
|
||||
name='config_%s' % config_page.url))
|
||||
|
@ -6,107 +6,126 @@
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django import forms
|
||||
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 openslides import get_version, get_git_commit_id, RELEASE
|
||||
from openslides.utils.views import FormView
|
||||
from openslides.utils.template import Tab
|
||||
from openslides.utils.views import FormView, TemplateView
|
||||
from .forms import GeneralConfigForm
|
||||
from .models import config
|
||||
from .api import config
|
||||
from .signals import config_signal
|
||||
|
||||
|
||||
class GeneralConfig(FormView):
|
||||
class ConfigView(FormView):
|
||||
"""
|
||||
Gereral config values.
|
||||
The view for a config page.
|
||||
"""
|
||||
permission_required = 'config.can_manage_config'
|
||||
form_class = GeneralConfigForm
|
||||
template_name = 'config/general.html'
|
||||
success_url_name = 'config_general'
|
||||
template_name = 'config/config_form.html'
|
||||
config_page = None
|
||||
form_class = forms.Form
|
||||
|
||||
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):
|
||||
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'
|
||||
template_name = 'config/version.html'
|
||||
initial = super(ConfigView, self).get_initial()
|
||||
for variable in self.config_page.variables:
|
||||
initial.update({variable.name: config[variable.name]})
|
||||
return initial
|
||||
|
||||
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.
|
||||
openslides_version_string = get_version()
|
||||
if not RELEASE:
|
||||
openslides_version_string += ' Commit: %s' % get_git_commit_id()
|
||||
context['versions'] = [('OpenSlides', openslides_version_string)]
|
||||
context['active_config_page'] = self.config_page
|
||||
|
||||
config_pages_list = []
|
||||
for receiver, config_page in config_signal.send(sender=self):
|
||||
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
|
||||
|
||||
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):
|
||||
"""
|
||||
Register the config tab.
|
||||
Registers the tab for this app in the main menu.
|
||||
"""
|
||||
selected = request.path.startswith('/config/')
|
||||
return Tab(
|
||||
title=_('Configuration'),
|
||||
app='config',
|
||||
url=reverse('config_general'),
|
||||
permission=request.user.has_perm('config.can_manage_config'),
|
||||
selected=selected,
|
||||
)
|
||||
url=reverse('config_first_config_page'),
|
||||
permission=request.user.has_perm('config.can_manage'),
|
||||
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 -*-
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
|
||||
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()
|
||||
|
||||
|
||||
@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',
|
||||
'openslides.participant.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'openslides.config.middleware.ConfigCacheMiddleware',
|
||||
)
|
||||
|
||||
ROOT_URLCONF = 'openslides.urls'
|
||||
@ -110,6 +111,7 @@ INSTALLED_APPS = (
|
||||
'mptt',
|
||||
'openslides.utils',
|
||||
'openslides.poll',
|
||||
'openslides.core',
|
||||
'openslides.projector',
|
||||
'openslides.agenda',
|
||||
'openslides.motion',
|
||||
|
@ -284,12 +284,10 @@ def run_syncdb():
|
||||
def set_system_url(url):
|
||||
# can't be imported in global scope as it already requires
|
||||
# the settings module during import
|
||||
from openslides.config.models import config
|
||||
from openslides.config.api import config
|
||||
|
||||
key = "participant_pdf_system_url"
|
||||
if key in config:
|
||||
return
|
||||
config[key] = url
|
||||
if config['participant_pdf_system_url'] == 'http://example.com:8000':
|
||||
config['participant_pdf_system_url'] = url
|
||||
|
||||
|
||||
def create_or_reset_admin_user():
|
||||
|
@ -7,9 +7,8 @@
|
||||
The OpenSlides motion app appends the functionality to OpenSlides to
|
||||
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.
|
||||
"""
|
||||
|
||||
import openslides.motion.signals
|
||||
import openslides.motion.slides
|
||||
from . import signals, slides
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
|
@ -15,7 +15,7 @@ from django.utils.translation import ugettext as _
|
||||
|
||||
from openslides.utils.forms import CssClassMixin
|
||||
from openslides.utils.person import PersonFormField, MultiplePersonFormField
|
||||
from .models import Motion, Workflow, Category
|
||||
from .models import Motion, Category
|
||||
|
||||
|
||||
class BaseMotionForm(forms.ModelForm, CssClassMixin):
|
||||
@ -100,55 +100,3 @@ class MotionIdentifierMixin(forms.ModelForm):
|
||||
"""Mixin to let the user choose the identifier for the motion."""
|
||||
|
||||
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
|
||||
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.
|
||||
"""
|
||||
|
||||
@ -24,8 +24,7 @@ from django.utils.translation import pgettext
|
||||
from django.utils.translation import ugettext_lazy, ugettext_noop, ugettext as _
|
||||
|
||||
from openslides.utils.person import PersonField
|
||||
from openslides.config.models import config
|
||||
from openslides.config.signals import default_config_value
|
||||
from openslides.config.api import config
|
||||
from openslides.poll.models import (
|
||||
BaseOption, BasePoll, CountVotesCast, CountInvalid, BaseVote)
|
||||
from openslides.participant.models import User
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
@ -16,9 +16,8 @@ from reportlab.platypus import (
|
||||
SimpleDocTemplate, PageBreak, Paragraph, Spacer, Table, TableStyle)
|
||||
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 .models import Motion
|
||||
|
||||
|
||||
|
@ -11,26 +11,110 @@
|
||||
"""
|
||||
|
||||
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 .models import Workflow, State
|
||||
|
||||
|
||||
@receiver(default_config_value, dispatch_uid="motion_default_config")
|
||||
def default_config(sender, key, **kwargs):
|
||||
"""Return the default config values for the motion app."""
|
||||
return {
|
||||
'motion_min_supporters': 0,
|
||||
'motion_preamble': _('The assembly may decide,'),
|
||||
'motion_pdf_ballot_papers_selection': 'CUSTOM_NUMBER',
|
||||
'motion_pdf_ballot_papers_number': '8',
|
||||
'motion_pdf_title': _('Motions'),
|
||||
'motion_pdf_preamble': '',
|
||||
'motion_allow_disable_versioning': False,
|
||||
'motion_workflow': 1}.get(key)
|
||||
@receiver(config_signal, dispatch_uid='setup_motion_config_page')
|
||||
def setup_motion_config_page(sender, **kwargs):
|
||||
"""
|
||||
Motion config variables.
|
||||
"""
|
||||
motion_min_supporters = ConfigVariable(
|
||||
name='motion_min_supporters',
|
||||
default_value=0,
|
||||
form_field=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 = 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')
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
|
@ -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.
|
||||
|
||||
: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.
|
||||
"""
|
||||
|
||||
|
@ -29,21 +29,17 @@ from openslides.utils.utils import html_strong, htmldiff
|
||||
from openslides.poll.views import PollFormView
|
||||
from openslides.projector.api import get_active_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 .models import (Motion, MotionSubmitter, MotionSupporter, MotionPoll,
|
||||
MotionVersion, State, WorkflowError, Category)
|
||||
from .forms import (BaseMotionForm, MotionSubmitterMixin, MotionSupporterMixin,
|
||||
MotionDisableVersioningMixin, ConfigForm, MotionCategoryMixin,
|
||||
MotionDisableVersioningMixin, MotionCategoryMixin,
|
||||
MotionIdentifierMixin)
|
||||
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):
|
||||
"""View, to list all motions."""
|
||||
permission_required = 'motion.can_see_motion'
|
||||
@ -590,49 +586,15 @@ class CategoryDeleteView(DeleteView):
|
||||
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):
|
||||
"""Return the motion tab."""
|
||||
# TODO: Find a bether way to set the selected var.
|
||||
selected = request.path.startswith('/motion/')
|
||||
# TODO: Find a better way to set the selected var.
|
||||
return Tab(
|
||||
title=_('Motions'),
|
||||
app='motion',
|
||||
url=reverse('motion_list'),
|
||||
permission=request.user.has_perm('motion.can_see_motion'),
|
||||
selected=selected,
|
||||
)
|
||||
selected=request.path.startswith('/motion/'))
|
||||
|
||||
|
||||
def get_widgets(request):
|
||||
|
@ -6,13 +6,13 @@
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
from django.utils.translation import ugettext_noop
|
||||
|
||||
import openslides.participant.signals
|
||||
from . import signals
|
||||
|
||||
|
||||
NAME = ugettext_noop('Participant')
|
||||
|
@ -6,13 +6,13 @@
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
from django import forms
|
||||
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 openslides.utils.forms import (
|
||||
@ -112,20 +112,3 @@ class UsersettingsForm(forms.ModelForm, CssClassMixin):
|
||||
class UserImportForm(forms.Form, CssClassMixin):
|
||||
csvfile = forms.FileField(widget=forms.FileInput(attrs={'size': '50'}),
|
||||
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.
|
||||
|
||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
||||
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||
: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.models import signals
|
||||
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.signals import receive_persons
|
||||
|
||||
from openslides.config.models import config
|
||||
from openslides.config.signals import default_config_value
|
||||
|
||||
from openslides.config.api import config
|
||||
from openslides.projector.api import register_slidemodel
|
||||
from openslides.projector.projector import SlideMixin
|
||||
|
||||
@ -226,19 +223,6 @@ def receive_persons(sender, **kwargs):
|
||||
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)
|
||||
def djangouser_post_save(sender, instance, signal, *args, **kwargs):
|
||||
try:
|
||||
|
@ -2,24 +2,66 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
openslides.participant.signals
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
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.auth.models import Permission
|
||||
|
||||
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
|
||||
|
||||
|
||||
@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')
|
||||
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')
|
||||
|
||||
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.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.
|
||||
|
||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
||||
:copyright: 2011–2013 by OpenSlides team, see AUTHORS.
|
||||
:license: GNU GPL, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
@ -41,14 +41,14 @@ from openslides.utils.utils import (
|
||||
from openslides.utils.views import (
|
||||
FormView, PDFView, CreateView, UpdateView, DeleteView, PermissionMixin,
|
||||
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.motion.models import Motion
|
||||
from openslides.assignment.models import Assignment
|
||||
from openslides.participant.api import gen_username, gen_password, import_users
|
||||
from openslides.participant.forms import (
|
||||
UserCreateForm, UserUpdateForm, UsersettingsForm,
|
||||
UserImportForm, GroupForm, ConfigForm)
|
||||
UserImportForm, GroupForm)
|
||||
from openslides.participant.models import User, Group
|
||||
|
||||
|
||||
@ -388,34 +388,6 @@ class GroupDeleteView(DeleteView):
|
||||
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):
|
||||
extra_content = {}
|
||||
try:
|
||||
@ -485,7 +457,7 @@ def user_settings_password(request):
|
||||
|
||||
def register_tab(request):
|
||||
"""
|
||||
Register the participant tab.
|
||||
Registers the participant tab.
|
||||
"""
|
||||
selected = request.path.startswith('/participant/')
|
||||
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.importlib import import_module
|
||||
|
||||
from openslides.config.models import config
|
||||
from openslides.config.api import config
|
||||
from openslides.projector.projector import SLIDE, Slide
|
||||
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
@ -14,8 +14,6 @@ from django.db import models
|
||||
from django.dispatch import receiver
|
||||
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.projector import SlideMixin
|
||||
|
||||
@ -70,16 +68,3 @@ class ProjectorOverlay(models.Model):
|
||||
if self.sid:
|
||||
return "%s on %s" % (self.def_name, self.sid)
|
||||
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.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
|
||||
|
||||
|
||||
SLIDE = {}
|
||||
|
||||
|
||||
|
@ -4,12 +4,68 @@
|
||||
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.
|
||||
"""
|
||||
|
||||
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'])
|
||||
|
||||
|
||||
@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.views import (
|
||||
TemplateView, RedirectView, CreateView, UpdateView, DeleteView, AjaxMixin)
|
||||
from openslides.config.models import config
|
||||
from openslides.config.api import config
|
||||
from .api import (
|
||||
get_active_slide, set_active_slide, projector_message_set,
|
||||
projector_message_delete, get_slide_from_sid, get_all_widgets,
|
||||
@ -224,8 +224,8 @@ class ProjectorEdit(RedirectView):
|
||||
if config['up'] < 0:
|
||||
config['up'] = int(config['up']) + 10
|
||||
elif direction == 'clean':
|
||||
config['up'] = 0
|
||||
config['bigger'] = 100
|
||||
config['up'] = 0 # TODO: Use default value from the signal instead of fix value here
|
||||
config['bigger'] = 100 # TODO: Use default value from the signal instead of fix value here
|
||||
|
||||
|
||||
class CountdownEdit(RedirectView):
|
||||
|
@ -98,7 +98,7 @@
|
||||
<hr />
|
||||
<footer>
|
||||
<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>
|
||||
</footer>
|
||||
</div><!--/content-->
|
||||
@ -112,6 +112,9 @@
|
||||
<script src="{% static 'javascript/bootstrap.min.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>
|
||||
{% for javascript in extra_javascript %}
|
||||
<script src="{% static javascript %}" type="text/javascript"></script>
|
||||
{% endfor %}
|
||||
{% block javascript %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
@ -14,15 +14,10 @@ from django.conf import settings
|
||||
from django.conf.urls import patterns, url, include
|
||||
from django.utils.importlib import import_module
|
||||
|
||||
from openslides.utils.views import RedirectView
|
||||
|
||||
|
||||
handler500 = 'openslides.utils.views.server_error'
|
||||
|
||||
urlpatterns = patterns('',
|
||||
# Redirect to dashboard URL
|
||||
url(r'^$', RedirectView.as_view(url='projector/dashboard'), name='home',),
|
||||
|
||||
(r'^agenda/', include('openslides.agenda.urls')),
|
||||
(r'^motion/', include('openslides.motion.urls')),
|
||||
(r'^assignment/', include('openslides.assignment.urls')),
|
||||
@ -69,3 +64,7 @@ urlpatterns += patterns('',
|
||||
name='password_change',
|
||||
),
|
||||
)
|
||||
|
||||
urlpatterns += patterns('',
|
||||
(r'^', include('openslides.core.urls')),
|
||||
)
|
||||
|
@ -11,7 +11,7 @@
|
||||
"""
|
||||
|
||||
from django.contrib.auth.models import Permission
|
||||
from openslides.config.models import config
|
||||
from openslides.config.api import config
|
||||
|
||||
|
||||
class AnonymousAuth(object):
|
||||
|
@ -23,7 +23,7 @@ from django.conf import settings
|
||||
from django.utils import formats
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from openslides.config.models import config
|
||||
from openslides.config.api import config
|
||||
|
||||
|
||||
# register new truetype fonts
|
||||
|
@ -11,7 +11,8 @@
|
||||
"""
|
||||
|
||||
from django import template
|
||||
from openslides.config.models import config
|
||||
from openslides.config.api import config
|
||||
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
@ -14,14 +14,16 @@
|
||||
from django.test import TestCase as _TestCase
|
||||
|
||||
from openslides.core.signals import post_database_setup
|
||||
from openslides.config.api import config
|
||||
|
||||
|
||||
class TestCase(_TestCase):
|
||||
"""
|
||||
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):
|
||||
return_value = super(TestCase, self)._pre_setup(*args, **kwargs)
|
||||
post_database_setup.send(sender=self)
|
||||
config.setup_cache()
|
||||
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.
|
||||
"""
|
||||
tabs = []
|
||||
if 'extra_stylefiles' in context:
|
||||
extra_stylefiles = context['extra_stylefiles']
|
||||
else:
|
||||
extra_stylefiles = []
|
||||
for app in settings.INSTALLED_APPS:
|
||||
try:
|
||||
@ -401,8 +404,6 @@ def send_register_tab(sender, request, context, **kwargs):
|
||||
extra_stylefiles.append(tab.stylefile)
|
||||
except (ImportError, AttributeError):
|
||||
continue
|
||||
|
||||
context.update({
|
||||
'tabs': tabs,
|
||||
'extra_stylefiles': extra_stylefiles,
|
||||
})
|
||||
'extra_stylefiles': extra_stylefiles})
|
||||
|
@ -191,3 +191,18 @@ class ViewTest(TestCase):
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.refreshItems()
|
||||
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.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.exceptions import WorkflowError
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user