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:
- Changed widget api. Used new metaclass.
- Changed api for plugins.
- Renamed config api classes.
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_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.projector.api import get_active_slide, get_active_object
from openslides.projector.projector import Overlay
@ -29,8 +29,8 @@ def validate_start_time(value):
# TODO: Reinsert the datepicker scripts in the template
@receiver(config_signal, dispatch_uid='setup_agenda_config_page')
def setup_agenda_config_page(sender, **kwargs):
@receiver(config_signal, dispatch_uid='setup_agenda_config')
def setup_agenda_config(sender, **kwargs):
"""
Agenda config variables.
"""
@ -66,15 +66,15 @@ def setup_agenda_config_page(sender, **kwargs):
'javascript/jquery-ui-sliderAccess.min.js',
'javascript/agenda-config-datepicker.js']
return ConfigPage(title=ugettext_noop('Agenda'),
url='agenda',
required_permission='config.can_manage',
weight=20,
variables=(agenda_start_event_date_time,
agenda_show_last_speakers,
agenda_couple_countdown_and_speakers),
extra_context={'extra_stylefiles': extra_stylefiles,
'extra_javascript': extra_javascript})
return ConfigCollection(title=ugettext_noop('Agenda'),
url='agenda',
required_permission='config.can_manage',
weight=20,
variables=(agenda_start_event_date_time,
agenda_show_last_speakers,
agenda_couple_countdown_and_speakers),
extra_context={'extra_stylefiles': extra_stylefiles,
'extra_javascript': extra_javascript})
@receiver(projector_overlays, dispatch_uid="agenda_list_of_speakers")

View File

@ -5,12 +5,12 @@ from django.dispatch import receiver
from django.utils.translation import ugettext as _
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
@receiver(config_signal, dispatch_uid='setup_assignment_config_page')
def setup_assignment_config_page(sender, **kwargs):
@receiver(config_signal, dispatch_uid='setup_assignment_config')
def setup_assignment_config(sender, **kwargs):
"""
Assignment config variables.
"""
@ -66,13 +66,13 @@ def setup_assignment_config_page(sender, **kwargs):
('votes', ugettext_lazy('Always one option per candidate')),
('yesnoabstain', ugettext_lazy('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))
return ConfigCollection(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))

View File

@ -35,8 +35,8 @@ class ConfigHandler(object):
pass
# Call on_change callback
for receiver, config_page in config_signal.send(sender='set_value'):
for config_variable in config_page.variables:
for receiver, config_collection in config_signal.send(sender='set_value'):
for config_variable in config_collection.variables:
if config_variable.name == key and config_variable.on_change:
config_variable.on_change()
break
@ -45,8 +45,8 @@ class ConfigHandler(object):
"""
Returns the default value for 'key'.
"""
for receiver, config_page in config_signal.send(sender='get_default'):
for config_variable in config_page.variables:
for receiver, config_collection in config_signal.send(sender='get_default'):
for config_variable in config_collection.variables:
if config_variable.name == key:
return config_variable.default_value
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.
"""
self._cache = {}
for receiver, config_page in config_signal.send(sender='setup_cache'):
for config_variable in config_page.variables:
for receiver, config_collection in config_signal.send(sender='setup_cache'):
for config_variable in config_collection.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
@ -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
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.
An abstract base class for simple and grouped config collections. The
attributes title and url are required for collections that should be
shown as a view. The attribute required_permission is used to set which
users can control the view showing the colletion. 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={}):
def __init__(self, title=None, url=None, required_permission=None, weight=0, extra_context={}):
self.title = title
self.url = url
self.required_permission = required_permission
@ -98,25 +100,29 @@ class ConfigBasePage(object):
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:
if variable.form_field is not None:
return True
is_shown = True
break
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
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.
via the variables attribute. The collection is shown as a view via the config
main menu entry if there is at least one variable with a form field.
"""
def __init__(self, groups, **kwargs):
self.groups = groups
super(ConfigGroupedPage, self).__init__(**kwargs)
super(ConfigGroupedCollection, self).__init__(**kwargs)
@property
def variables(self):
@ -125,15 +131,16 @@ class ConfigGroupedPage(ConfigBasePage):
yield variable
class ConfigPage(ConfigBasePage):
class ConfigCollection(ConfigBaseCollection):
"""
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.
A simple object class for a ungrouped config collection. Developers have
to set the variables (tuple) directly. The collection is shown as a view via
the config main menu entry if there is at least one variable with a
form field.
"""
def __init__(self, variables, **kwargs):
self.variables = variables
super(ConfigPage, self).__init__(**kwargs)
super(ConfigCollection, self).__init__(**kwargs)
class ConfigGroup(object):
@ -153,7 +160,7 @@ 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
argument 'form_field' has to be set if the variable should appear
on the ConfigView. The argument 'on_change' can get a callback
which is called every time, the variable is changed.
"""

View File

@ -8,12 +8,14 @@
{% block content %}
<h1>
{% trans 'Configuration' %}
<small>{% trans active_config_page.title %}</small>
<small>{% trans active_config_collection_view.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 %}">{% trans config_page_dict.config_page.title %}</a>
{% for config_collection_dict in config_collection_list %}
<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 %}
</div>
</div>
@ -43,7 +45,7 @@
{% endfor %}
<p>
{% 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>
<small>* {% trans 'required' %}</small>
</form>

View File

@ -11,11 +11,11 @@ urlpatterns = patterns(
'',
url(r'^$',
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'):
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))
for receiver, config_collection in config_signal.send(sender='config_urls'):
if config_collection.is_shown():
urlpatterns += patterns('', url(r'^%s/$' % config_collection.url,
ConfigView.as_view(config_collection=config_collection),
name='config_%s' % config_collection.url))

View File

@ -14,34 +14,34 @@ from .signals import config_signal
class ConfigView(FormView):
"""
The view for a config page.
The view for a config collection.
"""
template_name = 'config/config_form.html'
config_page = None
config_collection = None
form_class = forms.Form
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)
def get_form(self, *args):
"""
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)
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
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.
"""
for variable in self.config_page.variables:
for variable in self.config_collection.variables:
if variable.form_field is not None:
yield (variable.name, variable.form_field)
@ -51,54 +51,55 @@ class ConfigView(FormView):
as intial value for the form.
"""
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]})
return initial
def get_context_data(self, **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.
Adds to the context the active config view, a list of dictionaries
containing all config collections each with a flag which is true if its
view is the active one and adds a flag whether the config collection
has groups. Adds also extra_stylefiles and extra_javascript.
"""
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 = []
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)
config_collection_list = []
for receiver, config_collection in config_signal.send(sender=self):
if config_collection.is_shown():
config_collection_list.append({
'config_collection': config_collection,
'active': self.request.path == reverse('config_%s' % config_collection.url)})
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'):
context['groups'] = self.config_page.groups
if hasattr(self.config_collection, 'groups'):
context['groups'] = self.config_collection.groups
else:
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:
context['extra_stylefiles'].extend(self.config_page.extra_context['extra_stylefiles'])
context['extra_stylefiles'].extend(self.config_collection.extra_context['extra_stylefiles'])
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:
context['extra_javascript'].extend(self.config_page.extra_context['extra_javascript'])
context['extra_javascript'].extend(self.config_collection.extra_context['extra_javascript'])
else:
context['extra_javascript'] = self.config_page.extra_context['extra_javascript']
context['extra_javascript'] = self.config_collection.extra_context['extra_javascript']
return context
def get_success_url(self):
"""
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):
"""
@ -106,17 +107,17 @@ class ConfigView(FormView):
"""
for key in form.cleaned_data:
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)
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(
title=_('Configuration'),
app='config',
url=reverse('config_first_config_page'),
url=reverse('config_first_config_collection_view'),
permission=request.user.has_perm('config.can_manage'),
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_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.projector.api import update_projector
post_database_setup = Signal()
@receiver(config_signal, dispatch_uid='setup_general_config_page')
def setup_general_config_page(sender, **kwargs):
@receiver(config_signal, dispatch_uid='setup_general_config')
def setup_general_config(sender, **kwargs):
"""
General config variables for OpenSlides. They are grouped in 'Event',
'Welcome Widget' and 'System'.
@ -182,7 +182,7 @@ def setup_general_config_page(sender, **kwargs):
title=ugettext_lazy('System'),
variables=(system_enable_anonymous, system_url, system_wlan_ssid, system_wlan_password, system_wlan_encryption))
return ConfigGroupedPage(
return ConfigGroupedCollection(
title=ugettext_noop('General'),
url='general',
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_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.core.signals import post_database_setup
from .models import State, Workflow
@receiver(config_signal, dispatch_uid='setup_motion_config_page')
def setup_motion_config_page(sender, **kwargs):
@receiver(config_signal, dispatch_uid='setup_motion_config')
def setup_motion_config(sender, **kwargs):
"""
Motion config variables.
"""
@ -109,22 +109,22 @@ def setup_motion_config_page(sender, **kwargs):
('serially_numbered', ugettext_lazy('Serially numbered')),
('manually', ugettext_lazy('Set it manually'))]))
return ConfigPage(title=ugettext_noop('Motion'),
url='motion',
required_permission='config.can_manage',
weight=30,
variables=(motion_stop_submitting,
motion_min_supporters,
motion_remove_supporters,
motion_preamble,
motion_pdf_ballot_papers_selection,
motion_pdf_ballot_papers_number,
motion_pdf_title,
motion_pdf_preamble,
motion_pdf_paragraph_numbering,
motion_allow_disable_versioning,
motion_workflow,
motion_identifier))
return ConfigCollection(title=ugettext_noop('Motion'),
url='motion',
required_permission='config.can_manage',
weight=30,
variables=(motion_stop_submitting,
motion_min_supporters,
motion_remove_supporters,
motion_preamble,
motion_pdf_ballot_papers_selection,
motion_pdf_ballot_papers_number,
motion_pdf_title,
motion_pdf_preamble,
motion_pdf_paragraph_numbering,
motion_allow_disable_versioning,
motion_workflow,
motion_identifier))
@receiver(post_database_setup, dispatch_uid='motion_create_builtin_workflows')

View File

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

View File

@ -7,7 +7,7 @@ from django.dispatch import receiver, Signal
from django.template.loader import render_to_string
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 .projector import Overlay
@ -15,13 +15,12 @@ from .projector import Overlay
projector_overlays = Signal(providing_args=['request'])
@receiver(config_signal, dispatch_uid='setup_projector_config_variables')
def config_variables(sender, **kwargs):
@receiver(config_signal, dispatch_uid='setup_projector_config')
def setup_projector_config(sender, **kwargs):
"""
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
# 'callback'.
projector = ConfigVariable(
@ -68,8 +67,8 @@ def config_variables(sender, **kwargs):
name='pdf_fullscreen',
default_value=False)
return ConfigPage(
title='No title here', url='bar', required_permission=None, variables=(
return ConfigCollection(
required_permission=None, variables=(
projector, projector_message,
countdown_time, countdown_start_stamp, countdown_pause_stamp,
countdown_state, projector_scale, projector_scroll,

View File

@ -277,7 +277,7 @@ class ConfigTest(TestCase):
self.client = Client()
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/')
self.assertContains(response, 'timepicker.css', 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 mock import patch
from openslides.config.api import (config, ConfigGroup, ConfigGroupedPage,
ConfigPage, ConfigVariable)
from openslides.config.api import (config, ConfigCollection, ConfigGroup,
ConfigGroupedCollection, ConfigVariable)
from openslides.config.exceptions import ConfigError, ConfigNotFound
from openslides.config.signals import config_signal
from openslides.participant.models import User
@ -34,11 +34,11 @@ class HandleConfigTest(TestCase):
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')
config_signal.connect(set_simple_config_view_multiple_vars, dispatch_uid='set_simple_config_view_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')
config_signal.disconnect(set_simple_config_view_multiple_vars, dispatch_uid='set_simple_config_view_multiple_vars_for_testing')
def test_database_queries(self):
"""
@ -187,12 +187,50 @@ class ConfigFormTest(TestCase):
{'integer_var': 'bad_string_value'})
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/')
self.assertEqual(response.status_code, 404)
response = self.client_manager.get('/config/testgroupedpage1/')
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):
response = self.client_manager.get('/config/testgroupedpage1/')
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.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_views_abstract(self):
config_collection_dict = {}
for receiver, config_collection in config_signal.send(sender=self):
config_collection_dict[receiver.__name__] = config_collection
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/')
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)
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 ">\s*Config vars for testing 2\s*</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):
@receiver(config_signal, dispatch_uid='set_grouped_config_view_for_testing')
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
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
@ -278,57 +316,62 @@ def set_grouped_config_page(sender, **kwargs):
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),
extra_context={'extra_stylefiles': ['styles/test-config-sjNN56dFGDrg2.css'],
'extra_javascript': ['javascript/test-config-djg4dFGVslk4209f.js']})
return ConfigGroupedCollection(
title='Config vars for testing 1',
url='testgroupedpage1',
required_permission='config.can_manage',
weight=10000,
groups=(group_1, group_2),
extra_context={'extra_stylefiles': ['styles/test-config-sjNN56dFGDrg2.css'],
'extra_javascript': ['javascript/test-config-djg4dFGVslk4209f.js']})
@receiver(config_signal, dispatch_uid='set_simple_config_page_for_testing')
def set_simple_config_page(sender, **kwargs):
@receiver(config_signal, dispatch_uid='set_simple_config_view_for_testing')
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.
"""
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)))
return ConfigCollection(
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):
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',
url='testsimplepage2',
required_permission='No permission required',
variables=(ConfigVariable(name='multiple_config_var', default_value='foobar1'),
ConfigVariable(name='multiple_config_var', default_value='foobar2')))
return ConfigCollection(
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=''),))
@receiver(config_signal, dispatch_uid='set_simple_config_collection_disabled_view_for_testing')
def set_simple_config_collection_disabled_view(sender, **kwargs):
return ConfigCollection(
title='Ho5iengaoon5Hoht',
url='testsimplepage3',
required_permission='No permission required',
variables=(ConfigVariable(name='hidden_config_var_2', default_value=''),))
@receiver(config_signal, dispatch_uid='set_simple_config_page_with_callback_for_testing')
def set_simple_config_page_with_callback(sender, **kwargs):
@receiver(config_signal, dispatch_uid='set_simple_config_collection_with_callback_for_testing')
def set_simple_config_collection_with_callback(sender, **kwargs):
def callback():
raise Exception('Change callback dhcnfg34dlg06kdg successfully called.')
return ConfigPage(title='Hvndfhsbgkridfgdfg',
url='testsimplepage4',
required_permission='No permission required',
variables=(ConfigVariable(
name='var_with_callback_ghvnfjd5768gdfkwg0hm2',
default_value='',
on_change=callback),))
return ConfigCollection(
title='Hvndfhsbgkridfgdfg',
url='testsimplepage4',
required_permission='No permission required',
variables=(ConfigVariable(
name='var_with_callback_ghvnfjd5768gdfkwg0hm2',
default_value='',
on_change=callback),))