Merge pull request #1145 from normanjaeckel/ReworkConfig

Rework config
This commit is contained in:
Norman Jäckel 2013-12-06 01:53:53 -08:00
commit e718dd7b25
13 changed files with 244 additions and 191 deletions

View File

@ -15,6 +15,7 @@ Files:
Other: Other:
- Changed widget api. Used new metaclass. - Changed widget api. Used new metaclass.
- Changed api for plugins. - Changed api for plugins.
- Renamed config api classes.
Version 1.5.1 (unreleased) Version 1.5.1 (unreleased)

View File

@ -11,7 +11,7 @@ from django.template.loader import render_to_string
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy, ugettext_noop from django.utils.translation import ugettext_lazy, ugettext_noop
from openslides.config.api import config, ConfigPage, ConfigVariable from openslides.config.api import config, ConfigCollection, ConfigVariable
from openslides.config.signals import config_signal from openslides.config.signals import config_signal
from openslides.projector.api import get_active_slide, get_active_object from openslides.projector.api import get_active_slide, get_active_object
from openslides.projector.projector import Overlay from openslides.projector.projector import Overlay
@ -29,8 +29,8 @@ def validate_start_time(value):
# TODO: Reinsert the datepicker scripts in the template # TODO: Reinsert the datepicker scripts in the template
@receiver(config_signal, dispatch_uid='setup_agenda_config_page') @receiver(config_signal, dispatch_uid='setup_agenda_config')
def setup_agenda_config_page(sender, **kwargs): def setup_agenda_config(sender, **kwargs):
""" """
Agenda config variables. Agenda config variables.
""" """
@ -66,7 +66,7 @@ def setup_agenda_config_page(sender, **kwargs):
'javascript/jquery-ui-sliderAccess.min.js', 'javascript/jquery-ui-sliderAccess.min.js',
'javascript/agenda-config-datepicker.js'] 'javascript/agenda-config-datepicker.js']
return ConfigPage(title=ugettext_noop('Agenda'), return ConfigCollection(title=ugettext_noop('Agenda'),
url='agenda', url='agenda',
required_permission='config.can_manage', required_permission='config.can_manage',
weight=20, weight=20,

View File

@ -5,12 +5,12 @@ from django.dispatch import receiver
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy, ugettext_noop from django.utils.translation import ugettext_lazy, ugettext_noop
from openslides.config.api import ConfigPage, ConfigVariable from openslides.config.api import ConfigCollection, ConfigVariable
from openslides.config.signals import config_signal from openslides.config.signals import config_signal
@receiver(config_signal, dispatch_uid='setup_assignment_config_page') @receiver(config_signal, dispatch_uid='setup_assignment_config')
def setup_assignment_config_page(sender, **kwargs): def setup_assignment_config(sender, **kwargs):
""" """
Assignment config variables. Assignment config variables.
""" """
@ -66,7 +66,7 @@ def setup_assignment_config_page(sender, **kwargs):
('votes', ugettext_lazy('Always one option per candidate')), ('votes', ugettext_lazy('Always one option per candidate')),
('yesnoabstain', ugettext_lazy('Always Yes-No-Abstain per candidate'))))) ('yesnoabstain', ugettext_lazy('Always Yes-No-Abstain per candidate')))))
return ConfigPage(title=ugettext_noop('Elections'), return ConfigCollection(title=ugettext_noop('Elections'),
url='assignment', url='assignment',
required_permission='config.can_manage', required_permission='config.can_manage',
weight=40, weight=40,

View File

@ -35,8 +35,8 @@ class ConfigHandler(object):
pass pass
# Call on_change callback # Call on_change callback
for receiver, config_page in config_signal.send(sender='set_value'): for receiver, config_collection in config_signal.send(sender='set_value'):
for config_variable in config_page.variables: for config_variable in config_collection.variables:
if config_variable.name == key and config_variable.on_change: if config_variable.name == key and config_variable.on_change:
config_variable.on_change() config_variable.on_change()
break break
@ -45,8 +45,8 @@ class ConfigHandler(object):
""" """
Returns the default value for 'key'. Returns the default value for 'key'.
""" """
for receiver, config_page in config_signal.send(sender='get_default'): for receiver, config_collection in config_signal.send(sender='get_default'):
for config_variable in config_page.variables: for config_variable in config_collection.variables:
if config_variable.name == key: if config_variable.name == key:
return config_variable.default_value return config_variable.default_value
raise ConfigNotFound('The config variable %s was not found.' % key) raise ConfigNotFound('The config variable %s was not found.' % key)
@ -57,8 +57,8 @@ class ConfigHandler(object):
signal to get the default into the cache. signal to get the default into the cache.
""" """
self._cache = {} self._cache = {}
for receiver, config_page in config_signal.send(sender='setup_cache'): for receiver, config_collection in config_signal.send(sender='setup_cache'):
for config_variable in config_page.variables: for config_variable in config_collection.variables:
if config_variable.name in self._cache: if config_variable.name in self._cache:
raise ConfigError('Too many values for config variable %s found.' % config_variable.name) raise ConfigError('Too many values for config variable %s found.' % config_variable.name)
self._cache[config_variable.name] = config_variable.default_value self._cache[config_variable.name] = config_variable.default_value
@ -81,15 +81,17 @@ use x = config[...], to set it use config[...] = x.
""" """
class ConfigBasePage(object): class ConfigBaseCollection(object):
""" """
An abstract base class for simple and grouped config pages. The An abstract base class for simple and grouped config collections. The
attributes title and url are required. The attribute weight is used attributes title and url are required for collections that should be
for the order of the links in the submenu of the views. The attribute shown as a view. The attribute required_permission is used to set which
extra_context can be used to insert extra css and js files into the users can control the view showing the colletion. The attribute weight
template. 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={}): def __init__(self, title=None, url=None, required_permission=None, weight=0, extra_context={}):
self.title = title self.title = title
self.url = url self.url = url
self.required_permission = required_permission self.required_permission = required_permission
@ -98,25 +100,29 @@ class ConfigBasePage(object):
def is_shown(self): def is_shown(self):
""" """
Returns True if at least one variable of the page has a form field. Returns True if at least one variable of the collection has a form field.
""" """
for variable in self.variables: for variable in self.variables:
if variable.form_field is not None: if variable.form_field is not None:
return True is_shown = True
break
else: else:
return False is_shown = False
if is_shown and (self.title is None or self.url is None):
raise ConfigError('The config collection %s must have a title and an url attribute.' % self)
return is_shown
class ConfigGroupedPage(ConfigBasePage): class ConfigGroupedCollection(ConfigBaseCollection):
""" """
A simple object class for a grouped config page. Developers have to A simple object class for a grouped config collection. Developers have to
set the groups attribute (tuple). The config variables are available set the groups attribute (tuple). The config variables are available
via the variables attribute. The page is shown as view in the config via the variables attribute. The collection is shown as a view via the config
tab, if there is at least one variable with a form field. main menu entry if there is at least one variable with a form field.
""" """
def __init__(self, groups, **kwargs): def __init__(self, groups, **kwargs):
self.groups = groups self.groups = groups
super(ConfigGroupedPage, self).__init__(**kwargs) super(ConfigGroupedCollection, self).__init__(**kwargs)
@property @property
def variables(self): def variables(self):
@ -125,15 +131,16 @@ class ConfigGroupedPage(ConfigBasePage):
yield variable yield variable
class ConfigPage(ConfigBasePage): class ConfigCollection(ConfigBaseCollection):
""" """
A simple object class for a ungrouped config page. Developers have A simple object class for a ungrouped config collection. Developers have
to set the variables (tuple) directly. The page is shown as view in to set the variables (tuple) directly. The collection is shown as a view via
the config tab, if there is at least one variable with a form field. the config main menu entry if there is at least one variable with a
form field.
""" """
def __init__(self, variables, **kwargs): def __init__(self, variables, **kwargs):
self.variables = variables self.variables = variables
super(ConfigPage, self).__init__(**kwargs) super(ConfigCollection, self).__init__(**kwargs)
class ConfigGroup(object): class ConfigGroup(object):
@ -153,7 +160,7 @@ class ConfigVariable(object):
""" """
A simple object class to wrap new config variables. The keyword A simple object class to wrap new config variables. The keyword
arguments 'name' and 'default_value' are required. The keyword arguments 'name' and 'default_value' are required. The keyword
argument 'form_field' has to be set, if the variable should appear argument 'form_field' has to be set if the variable should appear
on the ConfigView. The argument 'on_change' can get a callback on the ConfigView. The argument 'on_change' can get a callback
which is called every time, the variable is changed. which is called every time, the variable is changed.
""" """

View File

@ -8,12 +8,14 @@
{% block content %} {% block content %}
<h1> <h1>
{% trans 'Configuration' %} {% trans 'Configuration' %}
<small>{% trans active_config_page.title %}</small> <small>{% trans active_config_collection_view.title %}</small>
<small class="pull-right"> <small class="pull-right">
<div class="btn-toolbar"> <div class="btn-toolbar">
<div class="btn-group"> <div class="btn-group">
{% for config_page_dict in config_pages_list %} {% for config_collection_dict in config_collection_list %}
<a href="/config/{{ config_page_dict.config_page.url }}/" class="btn btn-mini {% if config_page_dict.active %}active{% endif %}">{% trans config_page_dict.config_page.title %}</a> <a href="/config/{{ config_collection_dict.config_collection.url }}/" class="btn btn-mini {% if config_collection_dict.active %}active{% endif %}">
{% trans config_collection_dict.config_collection.title %}
</a>
{% endfor %} {% endfor %}
</div> </div>
</div> </div>
@ -43,7 +45,7 @@
{% endfor %} {% endfor %}
<p> <p>
{% include 'formbuttons_save.html' %} {% include 'formbuttons_save.html' %}
<a href="/config/{{ active_config_page.url }}/" class="btn">{% trans 'Cancel' %}</a> <a href="/config/{{ active_config_collection_view.url }}/" class="btn">{% trans 'Cancel' %}</a>
</p> </p>
<small>* {% trans 'required' %}</small> <small>* {% trans 'required' %}</small>
</form> </form>

View File

@ -11,11 +11,11 @@ urlpatterns = patterns(
'', '',
url(r'^$', url(r'^$',
RedirectView.as_view(url_name='config_general'), RedirectView.as_view(url_name='config_general'),
name='config_first_config_page') name='config_first_config_collection_view')
) )
for receiver, config_page in config_signal.send(sender='config_urls'): for receiver, config_collection in config_signal.send(sender='config_urls'):
if config_page.is_shown(): if config_collection.is_shown():
urlpatterns += patterns('', url(r'^%s/$' % config_page.url, urlpatterns += patterns('', url(r'^%s/$' % config_collection.url,
ConfigView.as_view(config_page=config_page), ConfigView.as_view(config_collection=config_collection),
name='config_%s' % config_page.url)) name='config_%s' % config_collection.url))

View File

@ -14,34 +14,34 @@ from .signals import config_signal
class ConfigView(FormView): class ConfigView(FormView):
""" """
The view for a config page. The view for a config collection.
""" """
template_name = 'config/config_form.html' template_name = 'config/config_form.html'
config_page = None config_collection = None
form_class = forms.Form form_class = forms.Form
def has_permission(self, *args, **kwargs): def has_permission(self, *args, **kwargs):
""" """
Ensures that only users with tab's permission can see this view. Ensures that only users with permission can see this view.
""" """
self.permission_required = self.config_page.required_permission self.permission_required = self.config_collection.required_permission
return super(ConfigView, self).has_permission(*args, **kwargs) return super(ConfigView, self).has_permission(*args, **kwargs)
def get_form(self, *args): def get_form(self, *args):
""" """
Gets the form for the view. Includes all form fields given by the Gets the form for the view. Includes all form fields given by the
tab's config objects. config collection.
""" """
form = super(ConfigView, self).get_form(*args) form = super(ConfigView, self).get_form(*args)
for name, field in self.generate_form_fields_from_config_page(): for name, field in self.generate_form_fields_from_config_collection():
form.fields[name] = field form.fields[name] = field
return form return form
def generate_form_fields_from_config_page(self): def generate_form_fields_from_config_collection(self):
""" """
Generates the fields for the get_form function. Generates the fields for the get_form function.
""" """
for variable in self.config_page.variables: for variable in self.config_collection.variables:
if variable.form_field is not None: if variable.form_field is not None:
yield (variable.name, variable.form_field) yield (variable.name, variable.form_field)
@ -51,54 +51,55 @@ class ConfigView(FormView):
as intial value for the form. as intial value for the form.
""" """
initial = super(ConfigView, self).get_initial() initial = super(ConfigView, self).get_initial()
for variable in self.config_page.variables: for variable in self.config_collection.variables:
initial.update({variable.name: config[variable.name]}) initial.update({variable.name: config[variable.name]})
return initial return initial
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
""" """
Adds to the context the active config tab, a list of dictionaries Adds to the context the active config view, a list of dictionaries
containing all config tabs each with a flag which is true if the containing all config collections each with a flag which is true if its
tab is the active one and adds a flag whether the config page has view is the active one and adds a flag whether the config collection
groups. Adds also extra_stylefiles and extra_javascript. has groups. Adds also extra_stylefiles and extra_javascript.
""" """
context = super(ConfigView, self).get_context_data(**kwargs) context = super(ConfigView, self).get_context_data(**kwargs)
context['active_config_page'] = self.config_page context['active_config_collection_view'] = self.config_collection
config_pages_list = [] config_collection_list = []
for receiver, config_page in config_signal.send(sender=self): for receiver, config_collection in config_signal.send(sender=self):
if config_page.is_shown(): if config_collection.is_shown():
config_pages_list.append({ config_collection_list.append({
'config_page': config_page, 'config_collection': config_collection,
'active': self.request.path == reverse('config_%s' % config_page.url)}) 'active': self.request.path == reverse('config_%s' % config_collection.url)})
context['config_pages_list'] = sorted(config_pages_list, key=lambda config_page_dict: config_page_dict['config_page'].weight) context['config_collection_list'] = sorted(
config_collection_list, key=lambda config_collection_dict: config_collection_dict['config_collection'].weight)
if hasattr(self.config_page, 'groups'): if hasattr(self.config_collection, 'groups'):
context['groups'] = self.config_page.groups context['groups'] = self.config_collection.groups
else: else:
context['groups'] = None context['groups'] = None
if 'extra_stylefiles' in self.config_page.extra_context: if 'extra_stylefiles' in self.config_collection.extra_context:
if 'extra_stylefiles' in context: if 'extra_stylefiles' in context:
context['extra_stylefiles'].extend(self.config_page.extra_context['extra_stylefiles']) context['extra_stylefiles'].extend(self.config_collection.extra_context['extra_stylefiles'])
else: else:
context['extra_stylefiles'] = self.config_page.extra_context['extra_stylefiles'] context['extra_stylefiles'] = self.config_collection.extra_context['extra_stylefiles']
if 'extra_javascript' in self.config_page.extra_context: if 'extra_javascript' in self.config_collection.extra_context:
if 'extra_javascript' in context: if 'extra_javascript' in context:
context['extra_javascript'].extend(self.config_page.extra_context['extra_javascript']) context['extra_javascript'].extend(self.config_collection.extra_context['extra_javascript'])
else: else:
context['extra_javascript'] = self.config_page.extra_context['extra_javascript'] context['extra_javascript'] = self.config_collection.extra_context['extra_javascript']
return context return context
def get_success_url(self): def get_success_url(self):
""" """
Returns the success url when changes are saved. Here it is the same Returns the success url when changes are saved. Here it is the same
url as the tab. url as the main menu entry.
""" """
return reverse('config_%s' % self.config_page.url) return reverse('config_%s' % self.config_collection.url)
def form_valid(self, form): def form_valid(self, form):
""" """
@ -106,17 +107,17 @@ class ConfigView(FormView):
""" """
for key in form.cleaned_data: for key in form.cleaned_data:
config[key] = form.cleaned_data[key] config[key] = form.cleaned_data[key]
messages.success(self.request, _('%s settings successfully saved.') % _(self.config_page.title)) messages.success(self.request, _('%s settings successfully saved.') % _(self.config_collection.title))
return super(ConfigView, self).form_valid(form) return super(ConfigView, self).form_valid(form)
def register_tab(request): def register_tab(request):
""" """
Registers the tab for this app in the main menu. Registers the entry for this app in the main menu.
""" """
return Tab( return Tab(
title=_('Configuration'), title=_('Configuration'),
app='config', app='config',
url=reverse('config_first_config_page'), url=reverse('config_first_config_collection_view'),
permission=request.user.has_perm('config.can_manage'), permission=request.user.has_perm('config.can_manage'),
selected=request.path.startswith('/config/')) selected=request.path.startswith('/config/'))

View File

@ -5,15 +5,15 @@ from django.dispatch import receiver, Signal
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy, ugettext_noop from django.utils.translation import ugettext_lazy, ugettext_noop
from openslides.config.api import ConfigGroup, ConfigGroupedPage, ConfigVariable from openslides.config.api import ConfigGroup, ConfigGroupedCollection, ConfigVariable
from openslides.config.signals import config_signal from openslides.config.signals import config_signal
from openslides.projector.api import update_projector from openslides.projector.api import update_projector
post_database_setup = Signal() post_database_setup = Signal()
@receiver(config_signal, dispatch_uid='setup_general_config_page') @receiver(config_signal, dispatch_uid='setup_general_config')
def setup_general_config_page(sender, **kwargs): def setup_general_config(sender, **kwargs):
""" """
General config variables for OpenSlides. They are grouped in 'Event', General config variables for OpenSlides. They are grouped in 'Event',
'Welcome Widget' and 'System'. 'Welcome Widget' and 'System'.
@ -182,7 +182,7 @@ def setup_general_config_page(sender, **kwargs):
title=ugettext_lazy('System'), title=ugettext_lazy('System'),
variables=(system_enable_anonymous, system_url, system_wlan_ssid, system_wlan_password, system_wlan_encryption)) variables=(system_enable_anonymous, system_url, system_wlan_ssid, system_wlan_password, system_wlan_encryption))
return ConfigGroupedPage( return ConfigGroupedCollection(
title=ugettext_noop('General'), title=ugettext_noop('General'),
url='general', url='general',
required_permission='config.can_manage', required_permission='config.can_manage',

View File

@ -5,15 +5,15 @@ from django.dispatch import receiver
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy, ugettext_noop from django.utils.translation import ugettext_lazy, ugettext_noop
from openslides.config.api import ConfigPage, ConfigVariable from openslides.config.api import ConfigCollection, ConfigVariable
from openslides.config.signals import config_signal from openslides.config.signals import config_signal
from openslides.core.signals import post_database_setup from openslides.core.signals import post_database_setup
from .models import State, Workflow from .models import State, Workflow
@receiver(config_signal, dispatch_uid='setup_motion_config_page') @receiver(config_signal, dispatch_uid='setup_motion_config')
def setup_motion_config_page(sender, **kwargs): def setup_motion_config(sender, **kwargs):
""" """
Motion config variables. Motion config variables.
""" """
@ -109,7 +109,7 @@ def setup_motion_config_page(sender, **kwargs):
('serially_numbered', ugettext_lazy('Serially numbered')), ('serially_numbered', ugettext_lazy('Serially numbered')),
('manually', ugettext_lazy('Set it manually'))])) ('manually', ugettext_lazy('Set it manually'))]))
return ConfigPage(title=ugettext_noop('Motion'), return ConfigCollection(title=ugettext_noop('Motion'),
url='motion', url='motion',
required_permission='config.can_manage', required_permission='config.can_manage',
weight=30, weight=30,

View File

@ -7,7 +7,7 @@ from django.dispatch import receiver
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy, ugettext_noop from django.utils.translation import ugettext_lazy, ugettext_noop
from openslides.config.api import ConfigPage, ConfigVariable from openslides.config.api import ConfigCollection, ConfigVariable
from openslides.config.signals import config_signal from openslides.config.signals import config_signal
from openslides.core.signals import post_database_setup from openslides.core.signals import post_database_setup
@ -15,8 +15,8 @@ from .api import create_or_reset_admin_user
from .models import Group from .models import Group
@receiver(config_signal, dispatch_uid='setup_participant_config_page') @receiver(config_signal, dispatch_uid='setup_participant_config')
def setup_participant_config_page(sender, **kwargs): def setup_participant_config(sender, **kwargs):
""" """
Participant config variables. Participant config variables.
""" """
@ -44,7 +44,7 @@ def setup_participant_config_page(sender, **kwargs):
label=ugettext_lazy('Sort participants by first name'), label=ugettext_lazy('Sort participants by first name'),
help_text=ugettext_lazy('Disable for sorting by last name'))) help_text=ugettext_lazy('Disable for sorting by last name')))
return ConfigPage(title=ugettext_noop('Participant'), return ConfigCollection(title=ugettext_noop('Participant'),
url='participant', url='participant',
required_permission='config.can_manage', required_permission='config.can_manage',
weight=50, weight=50,

View File

@ -7,7 +7,7 @@ from django.dispatch import receiver, Signal
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.datastructures import SortedDict from django.utils.datastructures import SortedDict
from openslides.config.api import config, ConfigPage, ConfigVariable from openslides.config.api import config, ConfigCollection, ConfigVariable
from openslides.config.signals import config_signal from openslides.config.signals import config_signal
from .projector import Overlay from .projector import Overlay
@ -15,13 +15,12 @@ from .projector import Overlay
projector_overlays = Signal(providing_args=['request']) projector_overlays = Signal(providing_args=['request'])
@receiver(config_signal, dispatch_uid='setup_projector_config_variables') @receiver(config_signal, dispatch_uid='setup_projector_config')
def config_variables(sender, **kwargs): def setup_projector_config(sender, **kwargs):
""" """
Projector config variables for OpenSlides. They are not shown on a Projector config variables for OpenSlides. They are not shown on a
config page. config view.
""" """
# The active slide. The config-value is a dictonary with at least the entry # The active slide. The config-value is a dictonary with at least the entry
# 'callback'. # 'callback'.
projector = ConfigVariable( projector = ConfigVariable(
@ -68,8 +67,8 @@ def config_variables(sender, **kwargs):
name='pdf_fullscreen', name='pdf_fullscreen',
default_value=False) default_value=False)
return ConfigPage( return ConfigCollection(
title='No title here', url='bar', required_permission=None, variables=( required_permission=None, variables=(
projector, projector_message, projector, projector_message,
countdown_time, countdown_start_stamp, countdown_pause_stamp, countdown_time, countdown_start_stamp, countdown_pause_stamp,
countdown_state, projector_scale, projector_scroll, countdown_state, projector_scale, projector_scroll,

View File

@ -277,7 +277,7 @@ class ConfigTest(TestCase):
self.client = Client() self.client = Client()
self.client.login(username='config_test_admin', password='default') self.client.login(username='config_test_admin', password='default')
def test_config_page_css_javascript(self): def test_config_collection_css_javascript(self):
response = self.client.get('/config/agenda/') response = self.client.get('/config/agenda/')
self.assertContains(response, 'timepicker.css', status_code=200) self.assertContains(response, 'timepicker.css', status_code=200)
self.assertContains(response, 'jquery-ui-timepicker-addon.min.js', status_code=200) self.assertContains(response, 'jquery-ui-timepicker-addon.min.js', status_code=200)

View File

@ -7,8 +7,8 @@ from django.dispatch import receiver
from django.test.client import Client from django.test.client import Client
from mock import patch from mock import patch
from openslides.config.api import (config, ConfigGroup, ConfigGroupedPage, from openslides.config.api import (config, ConfigCollection, ConfigGroup,
ConfigPage, ConfigVariable) ConfigGroupedCollection, ConfigVariable)
from openslides.config.exceptions import ConfigError, ConfigNotFound from openslides.config.exceptions import ConfigError, ConfigNotFound
from openslides.config.signals import config_signal from openslides.config.signals import config_signal
from openslides.participant.models import User from openslides.participant.models import User
@ -34,11 +34,11 @@ class HandleConfigTest(TestCase):
callable_obj=self.get_config_var, key='unknown_config_var') callable_obj=self.get_config_var, key='unknown_config_var')
def test_get_multiple_config_var_error(self): 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') config_signal.connect(set_simple_config_view_multiple_vars, dispatch_uid='set_simple_config_view_multiple_vars_for_testing')
self.assertRaisesMessage(expected_exception=ConfigError, self.assertRaisesMessage(expected_exception=ConfigError,
expected_message='Too many values for config variable multiple_config_var found.', expected_message='Too many values for config variable multiple_config_var found.',
callable_obj=config.setup_cache) callable_obj=config.setup_cache)
config_signal.disconnect(set_simple_config_page_multiple_vars, dispatch_uid='set_simple_config_page_multiple_vars_for_testing') config_signal.disconnect(set_simple_config_view_multiple_vars, dispatch_uid='set_simple_config_view_multiple_vars_for_testing')
def test_database_queries(self): def test_database_queries(self):
""" """
@ -187,12 +187,50 @@ class ConfigFormTest(TestCase):
{'integer_var': 'bad_string_value'}) {'integer_var': 'bad_string_value'})
self.assertContains(response=response, text='errorlist', status_code=200) self.assertContains(response=response, text='errorlist', status_code=200)
def test_disabled_config_page(self): def test_disabled_config_view(self):
response = self.client_manager.get('/config/testsimplepage3/') response = self.client_manager.get('/config/testsimplepage3/')
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
response = self.client_manager.get('/config/testgroupedpage1/') response = self.client_manager.get('/config/testgroupedpage1/')
self.assertNotContains(response=response, text='Ho5iengaoon5Hoht', status_code=200) self.assertNotContains(response=response, text='Ho5iengaoon5Hoht', status_code=200)
def test_improperly_configured_config_view(self):
from openslides.config import urls
collection = ConfigCollection(
title='Only a small title but no url ci6xahb8Chula0Thesho',
variables=(ConfigVariable(name='some_var_paiji9theiW8ooXivae6',
default_value='',
form_field=forms.CharField()),))
def setup_bad_config_view_one(sender, **kwargs):
return collection
config_signal.connect(setup_bad_config_view_one, dispatch_uid='setup_bad_config_view_one_for_testing')
self.assertRaisesMessage(
ConfigError,
'The config collection %s must have a title and an url attribute.' % repr(collection),
reload,
urls)
config_signal.disconnect(setup_bad_config_view_one, dispatch_uid='setup_bad_config_view_one_for_testing')
def test_improperly_configured_config_view_two(self):
from openslides.config import urls
collection = ConfigCollection(
url='only_url_ureiraeY1Oochuad7xei',
variables=(ConfigVariable(name='some_var_vuuC6eiXeiyae3ik4gie',
default_value='',
form_field=forms.CharField()),))
def setup_bad_config_view_two(sender, **kwargs):
return collection
config_signal.connect(setup_bad_config_view_two, dispatch_uid='setup_bad_config_view_twoe_for_testing')
self.assertRaisesMessage(
ConfigError,
'The config collection %s must have a title and an url attribute.' % repr(collection),
reload,
urls)
config_signal.disconnect(setup_bad_config_view_two, dispatch_uid='setup_bad_config_view_twoe_for_testing')
def test_extra_stylefiles(self): def test_extra_stylefiles(self):
response = self.client_manager.get('/config/testgroupedpage1/') response = self.client_manager.get('/config/testgroupedpage1/')
text = '<link href="/static/styles/test-config-sjNN56dFGDrg2.css" type="text/css" rel="stylesheet" />' text = '<link href="/static/styles/test-config-sjNN56dFGDrg2.css" type="text/css" rel="stylesheet" />'
@ -232,24 +270,24 @@ class ConfigWeightTest(TestCase):
self.client_manager = Client() self.client_manager = Client()
self.client_manager.login(username='config_test_manager', password='default') self.client_manager.login(username='config_test_manager', password='default')
def test_order_of_config_pages_abstract(self): def test_order_of_config_views_abstract(self):
config_page_dict = {} config_collection_dict = {}
for receiver, config_page in config_signal.send(sender=self): for receiver, config_collection in config_signal.send(sender=self):
config_page_dict[receiver.__name__] = config_page config_collection_dict[receiver.__name__] = config_collection
self.assertGreater(config_page_dict['set_grouped_config_page'].weight, config_page_dict['set_simple_config_page'].weight) self.assertGreater(config_collection_dict['set_grouped_config_view'].weight, config_collection_dict['set_simple_config_view'].weight)
def test_order_of_config_pages_on_view(self): def test_order_of_config_collections_on_view(self):
response = self.client_manager.get('/config/testgroupedpage1/') response = self.client_manager.get('/config/testgroupedpage1/')
import re import re
m1 = re.search('<a href="/config/testgroupedpage1/" class="btn btn-mini active">Config vars for testing 1</a>', response.content) m1 = re.search('<a href="/config/testgroupedpage1/" class="btn btn-mini active">\s*Config vars for testing 1\s*</a>', response.content)
m2 = re.search('<a href="/config/testsimplepage1/" class="btn btn-mini ">Config vars for testing 2</a>', response.content) m2 = re.search('<a href="/config/testsimplepage1/" class="btn btn-mini ">\s*Config vars for testing 2\s*</a>', response.content)
self.assertGreater(m1.start(), m2.start()) self.assertGreater(m1.start(), m2.start())
@receiver(config_signal, dispatch_uid='set_grouped_config_page_for_testing') @receiver(config_signal, dispatch_uid='set_grouped_config_view_for_testing')
def set_grouped_config_page(sender, **kwargs): def set_grouped_config_view(sender, **kwargs):
""" """
Sets a grouped config page which can be reached under the url Sets a grouped config collection view which can be reached under the url
'/config/testgroupedpage1/'. There are some variables, one variable '/config/testgroupedpage1/'. There are some variables, one variable
with a string as default value, one with a boolean as default value, 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 one with an integer as default value, one with choices and one
@ -278,7 +316,8 @@ def set_grouped_config_page(sender, **kwargs):
form_field=forms.ChoiceField(choices=((1, 'Choice One Ughoch4ocoche6Ee'), (2, 'Choice Two Vahnoh5yalohv5Eb')))) 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)) group_2 = ConfigGroup(title='Group 2 Toongai7ahyahy7B', variables=(hidden_var, choices_var))
return ConfigGroupedPage(title='Config vars for testing 1', return ConfigGroupedCollection(
title='Config vars for testing 1',
url='testgroupedpage1', url='testgroupedpage1',
required_permission='config.can_manage', required_permission='config.can_manage',
weight=10000, weight=10000,
@ -287,13 +326,14 @@ def set_grouped_config_page(sender, **kwargs):
'extra_javascript': ['javascript/test-config-djg4dFGVslk4209f.js']}) 'extra_javascript': ['javascript/test-config-djg4dFGVslk4209f.js']})
@receiver(config_signal, dispatch_uid='set_simple_config_page_for_testing') @receiver(config_signal, dispatch_uid='set_simple_config_view_for_testing')
def set_simple_config_page(sender, **kwargs): def set_simple_config_view(sender, **kwargs):
""" """
Sets a simple config page with some config variables but without Sets a simple config view with some config variables but without
grouping. grouping.
""" """
return ConfigPage(title='Config vars for testing 2', return ConfigCollection(
title='Config vars for testing 2',
url='testsimplepage1', url='testsimplepage1',
required_permission='No permission required', required_permission='No permission required',
variables=(ConfigVariable(name='additional_config_var', default_value='BaeB0ahcMae3feem'), variables=(ConfigVariable(name='additional_config_var', default_value='BaeB0ahcMae3feem'),
@ -302,30 +342,33 @@ def set_simple_config_page(sender, **kwargs):
# Do not connect to the signal now but later inside the test. # Do not connect to the signal now but later inside the test.
def set_simple_config_page_multiple_vars(sender, **kwargs): def set_simple_config_view_multiple_vars(sender, **kwargs):
""" """
Sets a bad config page with some multiple config vars. Sets a bad config view with some multiple config vars.
""" """
return ConfigPage(title='Config vars for testing 3', return ConfigCollection(
title='Config vars for testing 3',
url='testsimplepage2', url='testsimplepage2',
required_permission='No permission required', required_permission='No permission required',
variables=(ConfigVariable(name='multiple_config_var', default_value='foobar1'), variables=(ConfigVariable(name='multiple_config_var', default_value='foobar1'),
ConfigVariable(name='multiple_config_var', default_value='foobar2'))) ConfigVariable(name='multiple_config_var', default_value='foobar2')))
@receiver(config_signal, dispatch_uid='set_simple_config_page_disabled_page_for_testing') @receiver(config_signal, dispatch_uid='set_simple_config_collection_disabled_view_for_testing')
def set_simple_config_page_disabled_page(sender, **kwargs): def set_simple_config_collection_disabled_view(sender, **kwargs):
return ConfigPage(title='Ho5iengaoon5Hoht', return ConfigCollection(
title='Ho5iengaoon5Hoht',
url='testsimplepage3', url='testsimplepage3',
required_permission='No permission required', required_permission='No permission required',
variables=(ConfigVariable(name='hidden_config_var_2', default_value=''),)) variables=(ConfigVariable(name='hidden_config_var_2', default_value=''),))
@receiver(config_signal, dispatch_uid='set_simple_config_page_with_callback_for_testing') @receiver(config_signal, dispatch_uid='set_simple_config_collection_with_callback_for_testing')
def set_simple_config_page_with_callback(sender, **kwargs): def set_simple_config_collection_with_callback(sender, **kwargs):
def callback(): def callback():
raise Exception('Change callback dhcnfg34dlg06kdg successfully called.') raise Exception('Change callback dhcnfg34dlg06kdg successfully called.')
return ConfigPage(title='Hvndfhsbgkridfgdfg', return ConfigCollection(
title='Hvndfhsbgkridfgdfg',
url='testsimplepage4', url='testsimplepage4',
required_permission='No permission required', required_permission='No permission required',
variables=(ConfigVariable( variables=(ConfigVariable(