Merge pull request #1549 from normanjaeckel/ConfigRefactoring
Refactored config API. Removed form_fields. Added extra fields for HT…
This commit is contained in:
commit
a0f4506c35
@ -1,12 +1,12 @@
|
||||
from datetime import datetime
|
||||
|
||||
from django import forms
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.exceptions import ValidationError as DjangoValidationError
|
||||
from django.core.validators import MaxLengthValidator, MinValueValidator
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy, ugettext_noop
|
||||
from django.utils.translation import ugettext_lazy
|
||||
|
||||
from openslides.config.api import ConfigCollection, ConfigVariable
|
||||
from openslides.config.api import ConfigVariable
|
||||
|
||||
from .models import Item
|
||||
|
||||
@ -15,87 +15,72 @@ def validate_start_time(value):
|
||||
try:
|
||||
datetime.strptime(value, '%d.%m.%Y %H:%M')
|
||||
except ValueError:
|
||||
raise ValidationError(_('Invalid input.'))
|
||||
raise DjangoValidationError(_('Invalid input.'))
|
||||
|
||||
|
||||
# TODO: Reinsert the datepicker scripts in the template
|
||||
|
||||
def setup_agenda_config(sender, **kwargs):
|
||||
"""
|
||||
Receiver function to setup all agenda config variables. It is connected to
|
||||
the signal openslides.config.signals.config_signal during app loading.
|
||||
Receiver function to setup all agenda config variables. They are not
|
||||
grouped. This function connected to the signal
|
||||
openslides.config.signals.config_signal during app loading.
|
||||
"""
|
||||
# TODO: Insert validator for the format or use other field carefully.
|
||||
agenda_start_event_date_time = ConfigVariable(
|
||||
# TODO: Use an input type with generic datetime support.
|
||||
yield ConfigVariable(
|
||||
name='agenda_start_event_date_time',
|
||||
default_value='',
|
||||
form_field=forms.CharField(
|
||||
validators=[validate_start_time, ],
|
||||
widget=forms.DateTimeInput(format='%d.%m.%Y %H:%M'),
|
||||
required=False,
|
||||
label=ugettext_lazy('Begin of event'),
|
||||
help_text=ugettext_lazy('Input format: DD.MM.YYYY HH:MM')))
|
||||
help_text=ugettext_lazy('Input format: DD.MM.YYYY HH:MM'),
|
||||
weight=210,
|
||||
group=ugettext_lazy('Agenda'),
|
||||
validators=(validate_start_time,))
|
||||
|
||||
agenda_show_last_speakers = ConfigVariable(
|
||||
yield ConfigVariable(
|
||||
name='agenda_show_last_speakers',
|
||||
default_value=1,
|
||||
form_field=forms.IntegerField(
|
||||
min_value=0,
|
||||
label=ugettext_lazy('Number of last speakers to be shown on the projector')))
|
||||
input_type='integer',
|
||||
label=ugettext_lazy('Number of last speakers to be shown on the projector'),
|
||||
weight=220,
|
||||
group=ugettext_lazy('Agenda'),
|
||||
validators=(MinValueValidator(0),))
|
||||
|
||||
agenda_couple_countdown_and_speakers = ConfigVariable(
|
||||
yield ConfigVariable(
|
||||
name='agenda_couple_countdown_and_speakers',
|
||||
default_value=False,
|
||||
form_field=forms.BooleanField(
|
||||
input_type='boolean',
|
||||
label=ugettext_lazy('Couple countdown with the list of speakers'),
|
||||
help_text=ugettext_lazy('[Begin speach] starts the countdown, [End speach] stops the countdown.'),
|
||||
required=False))
|
||||
weight=230,
|
||||
group=ugettext_lazy('Agenda'))
|
||||
|
||||
agenda_number_prefix = ConfigVariable(
|
||||
yield ConfigVariable(
|
||||
name='agenda_number_prefix',
|
||||
default_value='',
|
||||
form_field=forms.CharField(
|
||||
label=ugettext_lazy('Numbering prefix for agenda items'),
|
||||
max_length=20,
|
||||
required=False))
|
||||
weight=240,
|
||||
group=ugettext_lazy('Agenda'),
|
||||
validators=(MaxLengthValidator(20),))
|
||||
|
||||
agenda_numeral_system = ConfigVariable(
|
||||
yield ConfigVariable(
|
||||
name='agenda_numeral_system',
|
||||
default_value='arabic',
|
||||
form_field=forms.ChoiceField(
|
||||
input_type='choice',
|
||||
label=ugettext_lazy('Numeral system for agenda items'),
|
||||
widget=forms.Select(),
|
||||
choices=(
|
||||
('arabic', ugettext_lazy('Arabic')),
|
||||
('roman', ugettext_lazy('Roman'))),
|
||||
required=False))
|
||||
|
||||
extra_stylefiles = ['css/jquery-ui-timepicker.css']
|
||||
extra_javascript = ['js/jquery/jquery-ui-timepicker-addon.min.js',
|
||||
'js/jquery/jquery-ui-sliderAccess.min.js',
|
||||
'js/jquery/datepicker-config.js']
|
||||
|
||||
return ConfigCollection(title=ugettext_noop('Agenda'),
|
||||
url='agenda',
|
||||
weight=20,
|
||||
variables=(agenda_start_event_date_time,
|
||||
agenda_show_last_speakers,
|
||||
agenda_couple_countdown_and_speakers,
|
||||
agenda_number_prefix,
|
||||
agenda_numeral_system),
|
||||
extra_context={'extra_stylefiles': extra_stylefiles,
|
||||
'extra_javascript': extra_javascript})
|
||||
{'value': 'arabic', 'display_name': ugettext_lazy('Arabic')},
|
||||
{'value': 'roman', 'display_name': ugettext_lazy('Roman')}),
|
||||
weight=250,
|
||||
group=ugettext_lazy('Agenda'))
|
||||
|
||||
|
||||
def listen_to_related_object_delete_signal(sender, instance, **kwargs):
|
||||
"""
|
||||
Receiver function to changed agenda items of a related items that is to
|
||||
Receiver function to change agenda items of a related item that is to
|
||||
be deleted. It is connected to the signal
|
||||
django.db.models.signals.pre_delete during app loading.
|
||||
"""
|
||||
if hasattr(instance, 'get_agenda_title'):
|
||||
for item in Item.objects.filter(content_type=ContentType.objects.get_for_model(sender), object_id=instance.pk):
|
||||
item.title = '< Item for deleted slide (%s) >' % instance.get_agenda_title()
|
||||
item.title = '< Item for deleted (%s) >' % instance.get_agenda_title()
|
||||
item.content_type = None
|
||||
item.object_id = None
|
||||
item.save()
|
||||
|
@ -1,97 +1,91 @@
|
||||
from django import forms
|
||||
from django.core.validators import MinValueValidator
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy, ugettext_noop
|
||||
from django.utils.translation import ugettext_lazy
|
||||
|
||||
from openslides.config.api import (
|
||||
ConfigGroup,
|
||||
ConfigGroupedCollection,
|
||||
ConfigVariable,
|
||||
)
|
||||
from openslides.config.api import ConfigVariable
|
||||
from openslides.poll.models import PERCENT_BASE_CHOICES
|
||||
|
||||
|
||||
def setup_assignment_config(sender, **kwargs):
|
||||
"""
|
||||
Receiver function to setup all assignment config variables. It is
|
||||
Receiver function to setup all assignment config variables. They are
|
||||
grouped in 'Ballot and ballot papers' and 'PDF'. This function is
|
||||
connected to the signal openslides.config.signals.config_signal during
|
||||
app loading.
|
||||
"""
|
||||
# Ballot and ballot papers
|
||||
assignments_poll_vote_values = ConfigVariable(
|
||||
|
||||
yield ConfigVariable(
|
||||
name='assignments_poll_vote_values',
|
||||
default_value='auto',
|
||||
form_field=forms.ChoiceField(
|
||||
widget=forms.Select(),
|
||||
required=False,
|
||||
input_type='choice',
|
||||
label=ugettext_lazy('Election method'),
|
||||
choices=(
|
||||
('auto', ugettext_lazy('Automatic assign of method')),
|
||||
('votes', ugettext_lazy('Always one option per candidate')),
|
||||
('yesnoabstain', ugettext_lazy('Always Yes-No-Abstain per candidate')))))
|
||||
assignments_poll_100_percent_base = ConfigVariable(
|
||||
{'value': 'auto', 'display_name': ugettext_lazy('Automatic assign of method')},
|
||||
{'value': 'votes', 'display_name': ugettext_lazy('Always one option per candidate')},
|
||||
{'value': 'yesnoabstain', 'display_name': ugettext_lazy('Always Yes-No-Abstain per candidate')}),
|
||||
weight=410,
|
||||
group=ugettext_lazy('Elections'),
|
||||
subgroup=ugettext_lazy('Ballot and ballot papers'))
|
||||
|
||||
yield ConfigVariable(
|
||||
name='assignments_poll_100_percent_base',
|
||||
default_value='WITHOUT_INVALID',
|
||||
form_field=forms.ChoiceField(
|
||||
widget=forms.Select(),
|
||||
required=False,
|
||||
input_type='choice',
|
||||
label=ugettext_lazy('The 100 % base of an election result consists of'),
|
||||
choices=PERCENT_BASE_CHOICES))
|
||||
assignments_pdf_ballot_papers_selection = ConfigVariable(
|
||||
choices=PERCENT_BASE_CHOICES,
|
||||
weight=420,
|
||||
group=ugettext_lazy('Elections'),
|
||||
subgroup=ugettext_lazy('Ballot and ballot papers'))
|
||||
|
||||
yield ConfigVariable(
|
||||
name='assignments_pdf_ballot_papers_selection',
|
||||
default_value='CUSTOM_NUMBER',
|
||||
form_field=forms.ChoiceField(
|
||||
widget=forms.Select(),
|
||||
required=False,
|
||||
input_type='choice',
|
||||
label=ugettext_lazy('Number of ballot papers (selection)'),
|
||||
choices=(
|
||||
('NUMBER_OF_DELEGATES', ugettext_lazy('Number of all delegates')),
|
||||
('NUMBER_OF_ALL_PARTICIPANTS', ugettext_lazy('Number of all participants')),
|
||||
('CUSTOM_NUMBER', ugettext_lazy('Use the following custom number')))))
|
||||
assignments_pdf_ballot_papers_number = ConfigVariable(
|
||||
{'value': 'NUMBER_OF_DELEGATES', 'display_name': ugettext_lazy('Number of all delegates')},
|
||||
{'value': 'NUMBER_OF_ALL_PARTICIPANTS', 'display_name': ugettext_lazy('Number of all participants')},
|
||||
{'value': 'CUSTOM_NUMBER', 'display_name': ugettext_lazy('Use the following custom number')}),
|
||||
weight=430,
|
||||
group=ugettext_lazy('Elections'),
|
||||
subgroup=ugettext_lazy('Ballot and ballot papers'))
|
||||
|
||||
yield ConfigVariable(
|
||||
name='assignments_pdf_ballot_papers_number',
|
||||
default_value=8,
|
||||
form_field=forms.IntegerField(
|
||||
widget=forms.TextInput(attrs={'class': 'small-input'}),
|
||||
required=False,
|
||||
min_value=1,
|
||||
label=ugettext_lazy('Custom number of ballot papers')))
|
||||
assignments_publish_winner_results_only = ConfigVariable(
|
||||
input_type='integer',
|
||||
label=ugettext_lazy('Custom number of ballot papers'),
|
||||
weight=440,
|
||||
group=ugettext_lazy('Elections'),
|
||||
subgroup=ugettext_lazy('Ballot and ballot papers'),
|
||||
validators=(MinValueValidator(1),))
|
||||
|
||||
yield ConfigVariable(
|
||||
name='assignments_publish_winner_results_only',
|
||||
default_value=False,
|
||||
form_field=forms.BooleanField(
|
||||
required=False,
|
||||
input_type='boolean',
|
||||
label=ugettext_lazy('Publish election result for elected candidates only '
|
||||
'(projector view)')))
|
||||
group_ballot = ConfigGroup(
|
||||
title=ugettext_lazy('Ballot and ballot papers'),
|
||||
variables=(assignments_poll_vote_values,
|
||||
assignments_poll_100_percent_base,
|
||||
assignments_pdf_ballot_papers_selection,
|
||||
assignments_pdf_ballot_papers_number,
|
||||
assignments_publish_winner_results_only))
|
||||
'(projector view)'),
|
||||
weight=450,
|
||||
group=ugettext_lazy('Elections'),
|
||||
subgroup=ugettext_lazy('Ballot and ballot papers'))
|
||||
|
||||
# PDF
|
||||
assignments_pdf_title = ConfigVariable(
|
||||
|
||||
yield ConfigVariable(
|
||||
name='assignments_pdf_title',
|
||||
default_value=_('Elections'),
|
||||
translatable=True,
|
||||
form_field=forms.CharField(
|
||||
widget=forms.TextInput(),
|
||||
required=False,
|
||||
label=ugettext_lazy('Title for PDF document (all elections)')))
|
||||
assignments_pdf_preamble = ConfigVariable(
|
||||
label=ugettext_lazy('Title for PDF document (all elections)'),
|
||||
weight=460,
|
||||
group=ugettext_lazy('Elections'),
|
||||
subgroup=ugettext_lazy('PDF'),
|
||||
translatable=True)
|
||||
|
||||
yield ConfigVariable(
|
||||
name='assignments_pdf_preamble',
|
||||
default_value='',
|
||||
form_field=forms.CharField(
|
||||
widget=forms.Textarea(),
|
||||
required=False,
|
||||
label=ugettext_lazy('Preamble text for PDF document (all elections)')))
|
||||
group_pdf = ConfigGroup(
|
||||
title=ugettext_lazy('PDF'),
|
||||
variables=(assignments_pdf_title, assignments_pdf_preamble))
|
||||
|
||||
return ConfigGroupedCollection(
|
||||
title=ugettext_noop('Elections'),
|
||||
url='assignment',
|
||||
weight=40,
|
||||
groups=(group_ballot, group_pdf))
|
||||
label=ugettext_lazy('Preamble text for PDF document (all elections)'),
|
||||
weight=470,
|
||||
group=ugettext_lazy('Elections'),
|
||||
subgroup=ugettext_lazy('PDF'))
|
||||
|
@ -1,74 +1,39 @@
|
||||
from django.core.exceptions import ValidationError as DjangoValidationError
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from .exceptions import ConfigError, ConfigNotFound
|
||||
from .models import ConfigStore
|
||||
from .signals import config_signal
|
||||
|
||||
INPUT_TYPE_MAPPING = {
|
||||
'string': str,
|
||||
'integer': int,
|
||||
'boolean': bool,
|
||||
'choice': str}
|
||||
|
||||
class ConfigHandler(object):
|
||||
|
||||
class ConfigHandler:
|
||||
"""
|
||||
An simple object class to wrap the config variables. It is a container
|
||||
A 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):
|
||||
"""
|
||||
Returns the value of the config variable. Builds the cache if it
|
||||
does not exist.
|
||||
"""
|
||||
try:
|
||||
return self._cache[key]
|
||||
except KeyError:
|
||||
raise ConfigNotFound('The config variable %s was not found.' % key)
|
||||
raise ConfigNotFound(_('The config variable %s was not found.') % key)
|
||||
except AttributeError:
|
||||
self.setup_cache()
|
||||
return self[key]
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
# Check if the variable is defined.
|
||||
if key not in self:
|
||||
raise ConfigNotFound('The config variable %s was not found.' % key)
|
||||
|
||||
# Save the new value to the database.
|
||||
updated_rows = ConfigStore.objects.filter(key=key).update(value=value)
|
||||
if not updated_rows:
|
||||
ConfigStore.objects.create(key=key, value=value)
|
||||
|
||||
# Update cache.
|
||||
self._cache[key] = value
|
||||
|
||||
# Call on_change callback.
|
||||
if self.get_config_variables()[key].on_change:
|
||||
self.get_config_variables()[key].on_change()
|
||||
|
||||
def items(self):
|
||||
"""
|
||||
Returns key-value pairs of all config variables.
|
||||
"""
|
||||
if not hasattr(self, '_cache'):
|
||||
self.setup_cache()
|
||||
return self._cache.items()
|
||||
|
||||
def get_config_variables(self):
|
||||
"""
|
||||
Returns a dictionary with all ConfigVariable instances of all
|
||||
collections. The key is the name of the config variables.
|
||||
"""
|
||||
result = {}
|
||||
for receiver, config_collection in config_signal.send(sender='get_config_variables'):
|
||||
for config_variable in config_collection.variables:
|
||||
if config_variable.name in result:
|
||||
raise ConfigError('Too many values for config variable %s found.' % config_variable.name)
|
||||
result[config_variable.name] = config_variable
|
||||
return result
|
||||
|
||||
def get_default(self, key):
|
||||
"""
|
||||
Returns the default value for 'key'.
|
||||
"""
|
||||
try:
|
||||
return self.get_config_variables()[key].default_value
|
||||
except KeyError:
|
||||
raise ConfigNotFound('The config variable %s was not found.' % key)
|
||||
|
||||
def setup_cache(self):
|
||||
"""
|
||||
Loads all config variables from the database by sending a signal to
|
||||
save the default to the cache.
|
||||
Creates a cache of all config variables with their current value.
|
||||
"""
|
||||
self._cache = {}
|
||||
for key, config_variable in self.get_config_variables().items():
|
||||
@ -84,6 +49,62 @@ class ConfigHandler(object):
|
||||
else:
|
||||
return True
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
"""
|
||||
Sets the new value. First it validates the input.
|
||||
"""
|
||||
# Check if the variable is defined.
|
||||
try:
|
||||
config_variable = config.get_config_variables()[key]
|
||||
except KeyError:
|
||||
raise ConfigNotFound(_('The config variable %s was not found.') % key)
|
||||
|
||||
# Validate datatype and run validators.
|
||||
expected_type = INPUT_TYPE_MAPPING[config_variable.input_type]
|
||||
if not isinstance(value, expected_type):
|
||||
raise ConfigError(_('Wrong datatype. Expected %s, got %s.') % (expected_type, type(value)))
|
||||
if config_variable.input_type == 'choice' and value not in map(lambda choice: choice['value'], config_variable.choices):
|
||||
raise ConfigError(_('Invalid input. Choice does not match.'))
|
||||
for validator in config_variable.validators:
|
||||
try:
|
||||
validator(value)
|
||||
except DjangoValidationError as e:
|
||||
raise ConfigError(e.messages[0])
|
||||
|
||||
# Save the new value to the database.
|
||||
updated_rows = ConfigStore.objects.filter(key=key).update(value=value)
|
||||
if not updated_rows:
|
||||
ConfigStore.objects.create(key=key, value=value)
|
||||
|
||||
# Update cache.
|
||||
if hasattr(self, '_cache'):
|
||||
self._cache[key] = value
|
||||
|
||||
# Call on_change callback.
|
||||
if config_variable.on_change:
|
||||
config_variable.on_change()
|
||||
|
||||
def items(self):
|
||||
"""
|
||||
Returns key-value pairs of all config variables.
|
||||
"""
|
||||
if not hasattr(self, '_cache'):
|
||||
self.setup_cache()
|
||||
return self._cache.items()
|
||||
|
||||
def get_config_variables(self):
|
||||
"""
|
||||
Returns a dictionary with all ConfigVariable instances of all
|
||||
signal receivers. The key is the name of the config variable.
|
||||
"""
|
||||
result = {}
|
||||
for receiver, config_collection in config_signal.send(sender='get_config_variables'):
|
||||
for config_variable in config_collection:
|
||||
if config_variable.name in result:
|
||||
raise ConfigError(_('Too many values for config variable %s found.') % config_variable.name)
|
||||
result[config_variable.name] = config_variable
|
||||
return result
|
||||
|
||||
def get_all_translatable(self):
|
||||
"""
|
||||
Generator to get all config variables as strings when their values are
|
||||
@ -100,92 +121,67 @@ use x = config[...], to set it use config[...] = x.
|
||||
"""
|
||||
|
||||
|
||||
class ConfigBaseCollection(object):
|
||||
class ConfigVariable:
|
||||
"""
|
||||
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 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.
|
||||
A simple object class to wrap new config variables.
|
||||
|
||||
The keyword arguments 'name' and 'default_value' are required.
|
||||
|
||||
The keyword arguments 'input_type', 'label' and 'help_text' are for
|
||||
rendering a HTML form element. If you set 'input_type' to 'choice' you
|
||||
have to provide 'choices', which is a list of dictionaries containing a
|
||||
value and a display_name of every possible choice.
|
||||
|
||||
The keyword arguments 'weight', 'group' and 'subgroup' are for sorting
|
||||
and grouping.
|
||||
|
||||
The keyword argument validators expects an interable of validator
|
||||
functions. Such a function gets the value and raises Django's
|
||||
ValidationError if the value is invalid.
|
||||
|
||||
The keyword argument 'on_change' can be a callback which is called
|
||||
every time, the variable is changed.
|
||||
|
||||
If the argument 'translatable' is set, OpenSlides is able to translate
|
||||
the value during setup of the database if the admin uses the respective
|
||||
command line option.
|
||||
"""
|
||||
def __init__(self, title=None, url=None, weight=0, extra_context={}):
|
||||
self.title = title
|
||||
self.url = url
|
||||
self.weight = weight
|
||||
self.extra_context = extra_context
|
||||
|
||||
def is_shown(self):
|
||||
"""
|
||||
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:
|
||||
is_shown = True
|
||||
break
|
||||
else:
|
||||
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 ConfigGroupedCollection(ConfigBaseCollection):
|
||||
"""
|
||||
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 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(ConfigGroupedCollection, self).__init__(**kwargs)
|
||||
|
||||
@property
|
||||
def variables(self):
|
||||
for group in self.groups:
|
||||
for variable in group.variables:
|
||||
yield variable
|
||||
|
||||
|
||||
class ConfigCollection(ConfigBaseCollection):
|
||||
"""
|
||||
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(ConfigCollection, 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. The argument 'on_change' can get a callback
|
||||
which is called every time, the variable is changed. If the argument
|
||||
'translatable' is set, OpenSlides is able to translate the value during
|
||||
setup of the database if the admin uses the respective command line option.
|
||||
"""
|
||||
def __init__(self, name, default_value, form_field=None, on_change=None, translatable=False):
|
||||
def __init__(self, name, default_value, input_type='string', label=None,
|
||||
help_text=None, choices=None, weight=0, group=None,
|
||||
subgroup=None, validators=None, on_change=None, translatable=False):
|
||||
if input_type not in INPUT_TYPE_MAPPING:
|
||||
raise ValueError(_('Invalid value for config attribute input_type.'))
|
||||
if input_type == 'choice' and choices is None:
|
||||
raise ConfigError(_("Either config attribute 'choices' must not be None or "
|
||||
"'input_type' must not be 'choice'."))
|
||||
elif input_type != 'choice' and choices is not None:
|
||||
raise ConfigError(_("Either config attribute 'choices' must be None or "
|
||||
"'input_type' must be 'choice'."))
|
||||
self.name = name
|
||||
self.default_value = default_value
|
||||
self.form_field = form_field
|
||||
self.input_type = input_type
|
||||
self.label = label or name
|
||||
self.help_text = help_text or ''
|
||||
self.choices = choices
|
||||
self.weight = weight
|
||||
self.group = group or _('General')
|
||||
self.subgroup = subgroup
|
||||
self.validators = validators or ()
|
||||
self.on_change = on_change
|
||||
self.translatable = translatable
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
"""
|
||||
Property with all data for OPTIONS requests.
|
||||
"""
|
||||
data = {
|
||||
'key': self.name,
|
||||
'value': config[self.name],
|
||||
'input_type': self.input_type,
|
||||
'label': self.label,
|
||||
'help_text': self.help_text
|
||||
}
|
||||
if self.input_type == 'choice':
|
||||
data['choices'] = self.choices
|
||||
return data
|
||||
|
@ -1,39 +1,72 @@
|
||||
from django.core.exceptions import ValidationError as DjangoValidationError
|
||||
from collections import OrderedDict
|
||||
from operator import attrgetter
|
||||
|
||||
from django.http import Http404
|
||||
|
||||
from openslides.utils.rest_api import Response, ValidationError, ViewSet
|
||||
from openslides.utils.rest_api import (
|
||||
Response,
|
||||
SimpleMetadata,
|
||||
ValidationError,
|
||||
ViewSet,
|
||||
)
|
||||
|
||||
from .api import config
|
||||
from .exceptions import ConfigNotFound
|
||||
from .exceptions import ConfigError, ConfigNotFound
|
||||
|
||||
|
||||
class ConfigMetadata(SimpleMetadata):
|
||||
"""
|
||||
Custom metadata class to add config info to responses on OPTIONS requests.
|
||||
"""
|
||||
def determine_metadata(self, request, view):
|
||||
# Sort config variables by weight.
|
||||
config_variables = sorted(config.get_config_variables().values(), key=attrgetter('weight'))
|
||||
|
||||
# Build tree.
|
||||
config_groups = []
|
||||
for config_variable in config_variables:
|
||||
if not config_groups or config_groups[-1]['name'] != config_variable.group:
|
||||
config_groups.append(OrderedDict(
|
||||
name=config_variable.group,
|
||||
subgroups=[]))
|
||||
if not config_groups[-1]['subgroups'] or config_groups[-1]['subgroups'][-1]['name'] != config_variable.subgroup:
|
||||
config_groups[-1]['subgroups'].append(OrderedDict(
|
||||
name=config_variable.subgroup,
|
||||
items=[]))
|
||||
config_groups[-1]['subgroups'][-1]['items'].append(config_variable.data)
|
||||
|
||||
# Add tree to metadata.
|
||||
metadata = super().determine_metadata(request, view)
|
||||
metadata['config_groups'] = config_groups
|
||||
return metadata
|
||||
|
||||
|
||||
class ConfigViewSet(ViewSet):
|
||||
"""
|
||||
API endpoint to list, retrieve and update the config.
|
||||
"""
|
||||
metadata_class = ConfigMetadata
|
||||
|
||||
def list(self, request):
|
||||
"""
|
||||
Lists all config variables. Everybody can see them.
|
||||
"""
|
||||
# TODO: Check if we need permission check here.
|
||||
data = ({'key': key, 'value': value} for key, value in config.items())
|
||||
return Response(data)
|
||||
return Response([{'key': key, 'value': value} for key, value in config.items()])
|
||||
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
"""
|
||||
Retrieves one config variable. Everybody can see it.
|
||||
Retrieves a config variable. Everybody can see it.
|
||||
"""
|
||||
# TODO: Check if we need permission check here.
|
||||
key = kwargs['pk']
|
||||
try:
|
||||
data = {'key': key, 'value': config[key]}
|
||||
value = config[key]
|
||||
except ConfigNotFound:
|
||||
raise Http404
|
||||
return Response(data)
|
||||
return Response({'key': key, 'value': value})
|
||||
|
||||
def update(self, request, *args, **kwargs):
|
||||
"""
|
||||
Updates one config variable. Only managers can do this.
|
||||
Updates a config variable. Only managers can do this.
|
||||
|
||||
Example: {"value": 42}
|
||||
"""
|
||||
@ -41,22 +74,16 @@ class ConfigViewSet(ViewSet):
|
||||
if not request.user.has_perm('config.can_manage'):
|
||||
self.permission_denied(request)
|
||||
|
||||
# Check if pk is a valid config variable key.
|
||||
key = kwargs['pk']
|
||||
if key not in config:
|
||||
raise Http404
|
||||
|
||||
# Validate value.
|
||||
form_field = config.get_config_variables()[key].form_field
|
||||
value = request.data['value']
|
||||
if form_field:
|
||||
try:
|
||||
form_field.clean(value)
|
||||
except DjangoValidationError as e:
|
||||
raise ValidationError({'detail': e.messages[0]})
|
||||
|
||||
# Change value.
|
||||
# Validate and change value.
|
||||
try:
|
||||
config[key] = value
|
||||
except ConfigNotFound:
|
||||
raise Http404
|
||||
except ConfigError as e:
|
||||
raise ValidationError({'detail': e})
|
||||
|
||||
# Return response.
|
||||
return Response({'key': key, 'value': value})
|
||||
|
@ -1,13 +1,9 @@
|
||||
from django import forms
|
||||
from django.core.validators import MaxLengthValidator
|
||||
from django.dispatch import Signal
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy, ugettext_noop
|
||||
from django.utils.translation import ugettext_lazy
|
||||
|
||||
from openslides.config.api import (
|
||||
ConfigGroup,
|
||||
ConfigGroupedCollection,
|
||||
ConfigVariable,
|
||||
)
|
||||
from openslides.config.api import ConfigVariable
|
||||
|
||||
# This signal is sent when the migrate command is done. That means it is sent
|
||||
# after post_migrate sending and creating all Permission objects. Don't use it
|
||||
@ -18,147 +14,123 @@ post_permission_creation = Signal()
|
||||
def setup_general_config(sender, **kwargs):
|
||||
"""
|
||||
Receiver function to setup general config variables for OpenSlides.
|
||||
They are grouped in 'Event', 'Projector' and 'System'. This function is
|
||||
connected to the signal openslides.config.signals.config_signal during
|
||||
app loading.
|
||||
There are two main groups: 'General' and 'Projector'. The group
|
||||
'General' has subgroups. This function is connected to the signal
|
||||
openslides.config.signals.config_signal during app loading.
|
||||
"""
|
||||
general_event_name = ConfigVariable(
|
||||
# General Event
|
||||
|
||||
yield ConfigVariable(
|
||||
name='general_event_name',
|
||||
default_value='OpenSlides',
|
||||
form_field=forms.CharField(
|
||||
widget=forms.TextInput(),
|
||||
label=ugettext_lazy('Event name'),
|
||||
max_length=50))
|
||||
weight=110,
|
||||
group=ugettext_lazy('General'),
|
||||
subgroup=ugettext_lazy('Event'),
|
||||
validators=(MaxLengthValidator(50),))
|
||||
|
||||
general_event_description = ConfigVariable(
|
||||
yield ConfigVariable(
|
||||
name='general_event_description',
|
||||
default_value=_('Presentation and assembly system'),
|
||||
translatable=True,
|
||||
form_field=forms.CharField(
|
||||
widget=forms.TextInput(),
|
||||
label=ugettext_lazy('Short description of event'),
|
||||
required=False,
|
||||
max_length=100))
|
||||
weight=115,
|
||||
group=ugettext_lazy('General'),
|
||||
subgroup=ugettext_lazy('Event'),
|
||||
validators=(MaxLengthValidator(100),),
|
||||
translatable=True)
|
||||
|
||||
general_event_date = ConfigVariable(
|
||||
yield ConfigVariable(
|
||||
name='general_event_date',
|
||||
default_value='',
|
||||
form_field=forms.CharField(
|
||||
widget=forms.TextInput(),
|
||||
label=ugettext_lazy('Event date'),
|
||||
required=False))
|
||||
weight=120,
|
||||
group=ugettext_lazy('General'),
|
||||
subgroup=ugettext_lazy('Event'))
|
||||
|
||||
general_event_location = ConfigVariable(
|
||||
yield ConfigVariable(
|
||||
name='general_event_location',
|
||||
default_value='',
|
||||
form_field=forms.CharField(
|
||||
widget=forms.TextInput(),
|
||||
label=ugettext_lazy('Event location'),
|
||||
required=False))
|
||||
weight=125,
|
||||
group=ugettext_lazy('General'),
|
||||
subgroup=ugettext_lazy('Event'))
|
||||
|
||||
# TODO: Check whether this variable is ever used.
|
||||
general_event_organizer = ConfigVariable(
|
||||
yield ConfigVariable(
|
||||
name='general_event_organizer',
|
||||
default_value='',
|
||||
form_field=forms.CharField(
|
||||
widget=forms.TextInput(),
|
||||
label=ugettext_lazy('Event organizer'),
|
||||
required=False))
|
||||
weight=130,
|
||||
group=ugettext_lazy('General'),
|
||||
subgroup=ugettext_lazy('Event'))
|
||||
|
||||
general_system_enable_anonymous = ConfigVariable(
|
||||
# General System
|
||||
|
||||
yield ConfigVariable(
|
||||
name='general_system_enable_anonymous',
|
||||
default_value=False,
|
||||
form_field=forms.BooleanField(
|
||||
input_type='boolean',
|
||||
label=ugettext_lazy('Allow access for anonymous guest users'),
|
||||
required=False))
|
||||
weight=135,
|
||||
group=ugettext_lazy('General'),
|
||||
subgroup=ugettext_lazy('System'))
|
||||
|
||||
projector_enable_logo = ConfigVariable(
|
||||
# Projector
|
||||
|
||||
yield ConfigVariable(
|
||||
name='projector_enable_logo',
|
||||
default_value=True,
|
||||
form_field=forms.BooleanField(
|
||||
input_type='boolean',
|
||||
label=ugettext_lazy('Show logo on projector'),
|
||||
help_text=ugettext_lazy('You can find and replace the logo under "openslides/projector/static/img/logo-projector.png".'),
|
||||
required=False))
|
||||
help_text=ugettext_lazy('You can find and replace the logo under "openslides/core/static/...".'), # TODO: Update path.
|
||||
weight=150,
|
||||
group=ugettext_lazy('Projector'))
|
||||
|
||||
projector_enable_title = ConfigVariable(
|
||||
yield ConfigVariable(
|
||||
name='projector_enable_title',
|
||||
default_value=True,
|
||||
form_field=forms.BooleanField(
|
||||
input_type='boolean',
|
||||
label=ugettext_lazy('Show title and description of event on projector'),
|
||||
required=False))
|
||||
weight=155,
|
||||
group=ugettext_lazy('Projector'))
|
||||
|
||||
projector_backgroundcolor1 = ConfigVariable(
|
||||
yield ConfigVariable(
|
||||
name='projector_backgroundcolor1',
|
||||
default_value='#444444',
|
||||
form_field=forms.CharField(
|
||||
widget=forms.TextInput(),
|
||||
label=ugettext_lazy('Background color of projector header'),
|
||||
help_text=ugettext_lazy('Use web color names like "red" or hex numbers like "#ff0000".'),
|
||||
required=True))
|
||||
weight=160,
|
||||
group=ugettext_lazy('Projector'))
|
||||
|
||||
projector_backgroundcolor2 = ConfigVariable(
|
||||
yield ConfigVariable(
|
||||
name='projector_backgroundcolor2',
|
||||
default_value='#222222',
|
||||
form_field=forms.CharField(
|
||||
widget=forms.TextInput(),
|
||||
label=ugettext_lazy('Second (optional) background color for linear color gradient'),
|
||||
help_text=ugettext_lazy('Use web color names like "red" or hex numbers like "#ff0000".'),
|
||||
required=False))
|
||||
weight=165,
|
||||
group=ugettext_lazy('Projector'))
|
||||
|
||||
projector_fontcolor = ConfigVariable(
|
||||
yield ConfigVariable(
|
||||
name='projector_fontcolor',
|
||||
default_value='#F5F5F5',
|
||||
form_field=forms.CharField(
|
||||
widget=forms.TextInput(),
|
||||
label=ugettext_lazy('Font color of projector header'),
|
||||
help_text=ugettext_lazy('Use web color names like "red" or hex numbers like "#ff0000".'),
|
||||
required=True))
|
||||
weight=170,
|
||||
group=ugettext_lazy('Projector'))
|
||||
|
||||
projector_welcome_title = ConfigVariable(
|
||||
yield ConfigVariable(
|
||||
name='projector_welcome_title',
|
||||
default_value=_('Welcome to OpenSlides'),
|
||||
translatable=True,
|
||||
form_field=forms.CharField(
|
||||
widget=forms.TextInput(),
|
||||
label=ugettext_lazy('Title'),
|
||||
help_text=ugettext_lazy('Also used for the default welcome slide.'),
|
||||
required=False))
|
||||
weight=175,
|
||||
group=ugettext_lazy('Projector'),
|
||||
translatable=True)
|
||||
|
||||
projector_welcome_text = ConfigVariable(
|
||||
yield ConfigVariable(
|
||||
name='projector_welcome_text',
|
||||
default_value=_('[Place for your welcome text.]'),
|
||||
translatable=True,
|
||||
form_field=forms.CharField(
|
||||
widget=forms.Textarea(),
|
||||
default_value=_('[Space for your welcome text.]'),
|
||||
label=ugettext_lazy('Welcome text'),
|
||||
required=False))
|
||||
|
||||
group_event = ConfigGroup(
|
||||
title=ugettext_lazy('Event'),
|
||||
variables=(
|
||||
general_event_name,
|
||||
general_event_description,
|
||||
general_event_date,
|
||||
general_event_location,
|
||||
general_event_organizer))
|
||||
|
||||
group_system = ConfigGroup(
|
||||
title=ugettext_lazy('System'),
|
||||
variables=(general_system_enable_anonymous,))
|
||||
|
||||
group_projector = ConfigGroup(
|
||||
title=ugettext_lazy('Projector'),
|
||||
variables=(
|
||||
projector_enable_logo,
|
||||
projector_enable_title,
|
||||
projector_backgroundcolor1,
|
||||
projector_backgroundcolor2,
|
||||
projector_fontcolor,
|
||||
projector_welcome_title,
|
||||
projector_welcome_text))
|
||||
|
||||
return ConfigGroupedCollection(
|
||||
title=ugettext_noop('General'),
|
||||
url='general',
|
||||
weight=10,
|
||||
groups=(group_event, group_system, group_projector))
|
||||
weight=180,
|
||||
group=ugettext_lazy('Projector'),
|
||||
translatable=True)
|
||||
|
@ -1,12 +1,8 @@
|
||||
from django import forms
|
||||
from django.core.validators import MinValueValidator
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import pgettext, ugettext_lazy, ugettext_noop
|
||||
|
||||
from openslides.config.api import (
|
||||
ConfigGroup,
|
||||
ConfigGroupedCollection,
|
||||
ConfigVariable,
|
||||
)
|
||||
from openslides.config.api import ConfigVariable
|
||||
from openslides.poll.models import PERCENT_BASE_CHOICES
|
||||
|
||||
from .models import State, Workflow
|
||||
@ -14,166 +10,165 @@ from .models import State, Workflow
|
||||
|
||||
def setup_motion_config(sender, **kwargs):
|
||||
"""
|
||||
Receiver function to setup all motion config variables. It is connected to
|
||||
the signal openslides.config.signals.config_signal during app loading.
|
||||
Receiver function to setup all motion config variables. They are
|
||||
grouped in 'General', 'Amendments', 'Supporters', 'Voting and ballot
|
||||
papers' and 'PDF'. This function connected to the signal
|
||||
openslides.config.signals.config_signal during app loading.
|
||||
"""
|
||||
# General
|
||||
motions_workflow = ConfigVariable(
|
||||
|
||||
yield ConfigVariable(
|
||||
name='motions_workflow',
|
||||
default_value='1',
|
||||
form_field=forms.ChoiceField(
|
||||
widget=forms.Select(),
|
||||
input_type='choice',
|
||||
label=ugettext_lazy('Workflow of new motions'),
|
||||
required=True,
|
||||
choices=[(str(workflow.pk), ugettext_lazy(workflow.name)) for workflow in Workflow.objects.all()]))
|
||||
motions_identifier = ConfigVariable(
|
||||
choices=({'value': str(workflow.pk), 'display_name': ugettext_lazy(workflow.name)} for workflow in Workflow.objects.all()),
|
||||
weight=310,
|
||||
group=ugettext_lazy('Motion'),
|
||||
subgroup=ugettext_lazy('General'))
|
||||
|
||||
yield ConfigVariable(
|
||||
name='motions_identifier',
|
||||
default_value='per_category',
|
||||
form_field=forms.ChoiceField(
|
||||
widget=forms.Select(),
|
||||
required=True,
|
||||
input_type='choice',
|
||||
label=ugettext_lazy('Identifier'),
|
||||
choices=[
|
||||
('per_category', ugettext_lazy('Numbered per category')),
|
||||
('serially_numbered', ugettext_lazy('Serially numbered')),
|
||||
('manually', ugettext_lazy('Set it manually'))]))
|
||||
motions_preamble = ConfigVariable(
|
||||
choices=(
|
||||
{'value': 'per_category', 'display_name': ugettext_lazy('Numbered per category')},
|
||||
{'value': 'serially_numbered', 'display_name': ugettext_lazy('Serially numbered')},
|
||||
{'value': 'manually', 'display_name': ugettext_lazy('Set it manually')}),
|
||||
weight=315,
|
||||
group=ugettext_lazy('Motion'),
|
||||
subgroup=ugettext_lazy('General'))
|
||||
|
||||
yield ConfigVariable(
|
||||
name='motions_preamble',
|
||||
default_value=_('The assembly may decide,'),
|
||||
translatable=True,
|
||||
form_field=forms.CharField(
|
||||
widget=forms.TextInput(),
|
||||
required=False,
|
||||
label=ugettext_lazy('Motion preamble')))
|
||||
motions_stop_submitting = ConfigVariable(
|
||||
label=ugettext_lazy('Motion preamble'),
|
||||
weight=320,
|
||||
group=ugettext_lazy('Motion'),
|
||||
subgroup=ugettext_lazy('General'),
|
||||
translatable=True)
|
||||
|
||||
yield ConfigVariable(
|
||||
name='motions_stop_submitting',
|
||||
default_value=False,
|
||||
form_field=forms.BooleanField(
|
||||
input_type='boolean',
|
||||
label=ugettext_lazy('Stop submitting new motions by non-staff users'),
|
||||
required=False))
|
||||
motions_allow_disable_versioning = ConfigVariable(
|
||||
weight=325,
|
||||
group=ugettext_lazy('Motion'),
|
||||
subgroup=ugettext_lazy('General'))
|
||||
|
||||
yield ConfigVariable(
|
||||
name='motions_allow_disable_versioning',
|
||||
default_value=False,
|
||||
form_field=forms.BooleanField(
|
||||
input_type='boolean',
|
||||
label=ugettext_lazy('Allow to disable versioning'),
|
||||
required=False))
|
||||
group_general = ConfigGroup(
|
||||
title=ugettext_lazy('General'),
|
||||
variables=(
|
||||
motions_workflow,
|
||||
motions_identifier,
|
||||
motions_preamble,
|
||||
motions_stop_submitting,
|
||||
motions_allow_disable_versioning))
|
||||
weight=330,
|
||||
group=ugettext_lazy('Motion'),
|
||||
subgroup=ugettext_lazy('General'))
|
||||
|
||||
# Amendments
|
||||
motions_amendments_enabled = ConfigVariable(
|
||||
|
||||
yield ConfigVariable(
|
||||
name='motions_amendments_enabled',
|
||||
default_value=False,
|
||||
form_field=forms.BooleanField(
|
||||
input_type='boolean',
|
||||
label=ugettext_lazy('Activate amendments'),
|
||||
required=False))
|
||||
weight=335,
|
||||
group=ugettext_lazy('Motion'),
|
||||
subgroup=ugettext_lazy('Amendments'))
|
||||
|
||||
motions_amendments_prefix = ConfigVariable(
|
||||
yield ConfigVariable(
|
||||
name='motions_amendments_prefix',
|
||||
default_value=pgettext('Prefix for the identifier for amendments', 'A'),
|
||||
form_field=forms.CharField(
|
||||
required=False,
|
||||
label=ugettext_lazy('Prefix for the identifier for amendments')))
|
||||
|
||||
group_amendments = ConfigGroup(
|
||||
title=ugettext_lazy('Amendments'),
|
||||
variables=(motions_amendments_enabled, motions_amendments_prefix))
|
||||
label=ugettext_lazy('Prefix for the identifier for amendments'),
|
||||
weight=340,
|
||||
group=ugettext_lazy('Motion'),
|
||||
subgroup=ugettext_lazy('Amendments'))
|
||||
|
||||
# Supporters
|
||||
motions_min_supporters = ConfigVariable(
|
||||
|
||||
yield ConfigVariable(
|
||||
name='motions_min_supporters',
|
||||
default_value=0,
|
||||
form_field=forms.IntegerField(
|
||||
widget=forms.TextInput(attrs={'class': 'small-input'}),
|
||||
input_type='integer',
|
||||
label=ugettext_lazy('Number of (minimum) required supporters for a motion'),
|
||||
min_value=0,
|
||||
help_text=ugettext_lazy('Choose 0 to disable the supporting system.')))
|
||||
motions_remove_supporters = ConfigVariable(
|
||||
help_text=ugettext_lazy('Choose 0 to disable the supporting system.'),
|
||||
weight=345,
|
||||
group=ugettext_lazy('Motion'),
|
||||
subgroup=ugettext_lazy('Supporters'),
|
||||
validators=(MinValueValidator(0),))
|
||||
|
||||
yield ConfigVariable(
|
||||
name='motions_remove_supporters',
|
||||
default_value=False,
|
||||
form_field=forms.BooleanField(
|
||||
input_type='boolean',
|
||||
label=ugettext_lazy('Remove all supporters of a motion if a submitter edits his motion in early state'),
|
||||
required=False))
|
||||
group_supporters = ConfigGroup(
|
||||
title=ugettext_lazy('Supporters'),
|
||||
variables=(motions_min_supporters, motions_remove_supporters))
|
||||
weight=350,
|
||||
group=ugettext_lazy('Motion'),
|
||||
subgroup=ugettext_lazy('Supporters'))
|
||||
|
||||
# Voting and ballot papers
|
||||
motions_poll_100_percent_base = ConfigVariable(
|
||||
|
||||
yield ConfigVariable(
|
||||
name='motions_poll_100_percent_base',
|
||||
default_value='WITHOUT_INVALID',
|
||||
form_field=forms.ChoiceField(
|
||||
widget=forms.Select(),
|
||||
required=False,
|
||||
input_type='choice',
|
||||
label=ugettext_lazy('The 100 % base of a voting result consists of'),
|
||||
choices=PERCENT_BASE_CHOICES))
|
||||
motions_pdf_ballot_papers_selection = ConfigVariable(
|
||||
choices=PERCENT_BASE_CHOICES,
|
||||
weight=355,
|
||||
group=ugettext_lazy('Motion'),
|
||||
subgroup=ugettext_lazy('Voting and ballot papers'))
|
||||
|
||||
yield ConfigVariable(
|
||||
name='motions_pdf_ballot_papers_selection',
|
||||
default_value='CUSTOM_NUMBER',
|
||||
form_field=forms.ChoiceField(
|
||||
widget=forms.Select(),
|
||||
required=False,
|
||||
input_type='choice',
|
||||
label=ugettext_lazy('Number of ballot papers (selection)'),
|
||||
choices=[
|
||||
('NUMBER_OF_DELEGATES', ugettext_lazy('Number of all delegates')),
|
||||
('NUMBER_OF_ALL_PARTICIPANTS', ugettext_lazy('Number of all participants')),
|
||||
('CUSTOM_NUMBER', ugettext_lazy("Use the following custom number"))]))
|
||||
motions_pdf_ballot_papers_number = ConfigVariable(
|
||||
choices=(
|
||||
{'value': 'NUMBER_OF_DELEGATES', 'display_name': ugettext_lazy('Number of all delegates')},
|
||||
{'value': 'NUMBER_OF_ALL_PARTICIPANTS', 'display_name': ugettext_lazy('Number of all participants')},
|
||||
{'value': 'CUSTOM_NUMBER', 'display_name': ugettext_lazy('Use the following custom number')}),
|
||||
weight=360,
|
||||
group=ugettext_lazy('Motion'),
|
||||
subgroup=ugettext_lazy('Voting and ballot papers'))
|
||||
|
||||
yield ConfigVariable(
|
||||
name='motions_pdf_ballot_papers_number',
|
||||
default_value=8,
|
||||
form_field=forms.IntegerField(
|
||||
widget=forms.TextInput(attrs={'class': 'small-input'}),
|
||||
required=False,
|
||||
min_value=1,
|
||||
label=ugettext_lazy('Custom number of ballot papers')))
|
||||
group_ballot_papers = ConfigGroup(
|
||||
title=ugettext_lazy('Voting and ballot papers'),
|
||||
variables=(
|
||||
motions_poll_100_percent_base,
|
||||
motions_pdf_ballot_papers_selection,
|
||||
motions_pdf_ballot_papers_number))
|
||||
input_type='integer',
|
||||
label=ugettext_lazy('Custom number of ballot papers'),
|
||||
weight=365,
|
||||
group=ugettext_lazy('Motion'),
|
||||
subgroup=ugettext_lazy('Voting and ballot papers'),
|
||||
validators=(MinValueValidator(1),))
|
||||
|
||||
# PDF
|
||||
motions_pdf_title = ConfigVariable(
|
||||
|
||||
yield ConfigVariable(
|
||||
name='motions_pdf_title',
|
||||
default_value=_('Motions'),
|
||||
translatable=True,
|
||||
form_field=forms.CharField(
|
||||
widget=forms.TextInput(),
|
||||
required=False,
|
||||
label=ugettext_lazy('Title for PDF document (all motions)')))
|
||||
motions_pdf_preamble = ConfigVariable(
|
||||
label=ugettext_lazy('Title for PDF document (all motions)'),
|
||||
weight=370,
|
||||
group=ugettext_lazy('Motion'),
|
||||
subgroup=ugettext_lazy('PDF'),
|
||||
translatable=True)
|
||||
|
||||
yield ConfigVariable(
|
||||
name='motions_pdf_preamble',
|
||||
default_value='',
|
||||
form_field=forms.CharField(
|
||||
widget=forms.Textarea(),
|
||||
required=False,
|
||||
label=ugettext_lazy('Preamble text for PDF document (all motions)')))
|
||||
motions_pdf_paragraph_numbering = ConfigVariable(
|
||||
label=ugettext_lazy('Preamble text for PDF document (all motions)'),
|
||||
weight=375,
|
||||
group=ugettext_lazy('Motion'),
|
||||
subgroup=ugettext_lazy('PDF'))
|
||||
|
||||
yield ConfigVariable(
|
||||
name='motions_pdf_paragraph_numbering',
|
||||
default_value=False,
|
||||
form_field=forms.BooleanField(
|
||||
label=ugettext_lazy('Show paragraph numbering (only in PDF)'),
|
||||
required=False))
|
||||
group_pdf = ConfigGroup(
|
||||
title=ugettext_lazy('PDF'),
|
||||
variables=(
|
||||
motions_pdf_title,
|
||||
motions_pdf_preamble,
|
||||
motions_pdf_paragraph_numbering))
|
||||
|
||||
return ConfigGroupedCollection(
|
||||
title=ugettext_noop('Motion'),
|
||||
url='motion',
|
||||
weight=30,
|
||||
groups=(group_general, group_amendments, group_supporters,
|
||||
group_ballot_papers, group_pdf))
|
||||
weight=380,
|
||||
group=ugettext_lazy('Motion'),
|
||||
subgroup=ugettext_lazy('PDF'))
|
||||
|
||||
|
||||
def create_builtin_workflows(sender, **kwargs):
|
||||
|
@ -68,9 +68,9 @@ class BaseVote(models.Model):
|
||||
|
||||
|
||||
PERCENT_BASE_CHOICES = (
|
||||
('WITHOUT_INVALID', ugettext_lazy('Only all valid votes')),
|
||||
('WITH_INVALID', ugettext_lazy('All votes cast (including invalid votes)')),
|
||||
('DISABLED', ugettext_lazy('Disabled (no percents)')))
|
||||
{'value': 'WITHOUT_INVALID', 'display_name': ugettext_lazy('Only all valid votes')},
|
||||
{'value': 'WITH_INVALID', 'display_name': ugettext_lazy('All votes cast (including invalid votes)')},
|
||||
{'value': 'DISABLED', 'display_name': ugettext_lazy('Disabled (no percents)')})
|
||||
|
||||
|
||||
class CollectDefaultVotesMixin(models.Model):
|
||||
|
@ -1,109 +1,93 @@
|
||||
from django import forms
|
||||
from django.db.models import Q
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy, ugettext_noop
|
||||
|
||||
from openslides.config.api import (
|
||||
ConfigGroup,
|
||||
ConfigGroupedCollection,
|
||||
ConfigVariable,
|
||||
)
|
||||
from openslides.config.api import ConfigVariable
|
||||
|
||||
from .models import Group, Permission, User
|
||||
|
||||
|
||||
def setup_users_config(sender, **kwargs):
|
||||
"""
|
||||
Receiver function to setup all users config variables. It is connected
|
||||
to the signal openslides.config.signals.config_signal during app loading.
|
||||
Receiver function to setup all users config variables. They are grouped
|
||||
in 'Sorting' and 'PDF'. This function is connected to the signal
|
||||
openslides.config.signals.config_signal during app loading.
|
||||
"""
|
||||
# General
|
||||
users_sort_users_by_first_name = ConfigVariable(
|
||||
|
||||
# Sorting
|
||||
|
||||
yield ConfigVariable(
|
||||
name='users_sort_users_by_first_name',
|
||||
default_value=False,
|
||||
form_field=forms.BooleanField(
|
||||
required=False,
|
||||
input_type='boolean',
|
||||
label=ugettext_lazy('Sort users by first name'),
|
||||
help_text=ugettext_lazy('Disable for sorting by last name')))
|
||||
|
||||
group_general = ConfigGroup(
|
||||
title=ugettext_lazy('Sorting'),
|
||||
variables=(users_sort_users_by_first_name,))
|
||||
help_text=ugettext_lazy('Disable for sorting by last name'),
|
||||
weight=510,
|
||||
group=ugettext_lazy('Users'),
|
||||
subgroup=ugettext_lazy('Sorting'))
|
||||
|
||||
# PDF
|
||||
users_pdf_welcometitle = ConfigVariable(
|
||||
|
||||
yield ConfigVariable(
|
||||
name='users_pdf_welcometitle',
|
||||
default_value=_('Welcome to OpenSlides!'),
|
||||
translatable=True,
|
||||
form_field=forms.CharField(
|
||||
widget=forms.Textarea(),
|
||||
required=False,
|
||||
label=ugettext_lazy('Title for access data and welcome PDF')))
|
||||
label=ugettext_lazy('Title for access data and welcome PDF'),
|
||||
weight=520,
|
||||
group=ugettext_lazy('Users'),
|
||||
subgroup=ugettext_lazy('PDF'),
|
||||
translatable=True)
|
||||
|
||||
users_pdf_welcometext = ConfigVariable(
|
||||
yield ConfigVariable(
|
||||
name='users_pdf_welcometext',
|
||||
default_value=_('[Place for your welcome and help text.]'),
|
||||
translatable=True,
|
||||
form_field=forms.CharField(
|
||||
widget=forms.Textarea(),
|
||||
required=False,
|
||||
label=ugettext_lazy('Help text for access data and welcome PDF')))
|
||||
label=ugettext_lazy('Help text for access data and welcome PDF'),
|
||||
weight=530,
|
||||
group=ugettext_lazy('Users'),
|
||||
subgroup=ugettext_lazy('PDF'),
|
||||
translatable=True)
|
||||
|
||||
users_pdf_url = ConfigVariable(
|
||||
# TODO: Use Django's URLValidator here.
|
||||
yield ConfigVariable(
|
||||
name='users_pdf_url',
|
||||
default_value='http://example.com:8000',
|
||||
form_field=forms.CharField(
|
||||
widget=forms.TextInput(),
|
||||
required=False,
|
||||
label=ugettext_lazy('System URL'),
|
||||
help_text=ugettext_lazy('Used for QRCode in PDF of access data.')))
|
||||
help_text=ugettext_lazy('Used for QRCode in PDF of access data.'),
|
||||
weight=540,
|
||||
group=ugettext_lazy('Users'),
|
||||
subgroup=ugettext_lazy('PDF'))
|
||||
|
||||
users_pdf_wlan_ssid = ConfigVariable(
|
||||
yield ConfigVariable(
|
||||
name='users_pdf_wlan_ssid',
|
||||
default_value='',
|
||||
form_field=forms.CharField(
|
||||
widget=forms.TextInput(),
|
||||
required=False,
|
||||
label=ugettext_lazy('WLAN name (SSID)'),
|
||||
help_text=ugettext_lazy('Used for WLAN QRCode in PDF of access data.')))
|
||||
help_text=ugettext_lazy('Used for WLAN QRCode in PDF of access data.'),
|
||||
weight=550,
|
||||
group=ugettext_lazy('Users'),
|
||||
subgroup=ugettext_lazy('PDF'))
|
||||
|
||||
users_pdf_wlan_password = ConfigVariable(
|
||||
yield ConfigVariable(
|
||||
name='users_pdf_wlan_password',
|
||||
default_value='',
|
||||
form_field=forms.CharField(
|
||||
widget=forms.TextInput(),
|
||||
required=False,
|
||||
label=ugettext_lazy('WLAN password'),
|
||||
help_text=ugettext_lazy('Used for WLAN QRCode in PDF of access data.')))
|
||||
help_text=ugettext_lazy('Used for WLAN QRCode in PDF of access data.'),
|
||||
weight=560,
|
||||
group=ugettext_lazy('Users'),
|
||||
subgroup=ugettext_lazy('PDF'))
|
||||
|
||||
users_pdf_wlan_encryption = ConfigVariable(
|
||||
yield ConfigVariable(
|
||||
name='users_pdf_wlan_encryption',
|
||||
default_value='',
|
||||
form_field=forms.ChoiceField(
|
||||
widget=forms.Select(),
|
||||
required=False,
|
||||
input_type='choice',
|
||||
label=ugettext_lazy('WLAN encryption'),
|
||||
help_text=ugettext_lazy('Used for WLAN QRCode in PDF of access data.'),
|
||||
choices=(
|
||||
('', '---------'),
|
||||
('WEP', 'WEP'),
|
||||
('WPA', 'WPA/WPA2'),
|
||||
('nopass', ugettext_lazy('No encryption')))))
|
||||
|
||||
group_pdf = ConfigGroup(
|
||||
title=ugettext_lazy('PDF'),
|
||||
variables=(users_pdf_welcometitle,
|
||||
users_pdf_welcometext,
|
||||
users_pdf_url,
|
||||
users_pdf_wlan_ssid,
|
||||
users_pdf_wlan_password,
|
||||
users_pdf_wlan_encryption))
|
||||
|
||||
return ConfigGroupedCollection(
|
||||
title=ugettext_noop('Users'),
|
||||
url='users',
|
||||
weight=50,
|
||||
groups=(group_general, group_pdf))
|
||||
{'value': '', 'display_name': '---------'},
|
||||
{'value': 'WEP', 'display_name': ugettext_lazy('WEP')},
|
||||
{'value': 'WPA', 'display_name': ugettext_lazy('WPA/WPA2')},
|
||||
{'value': 'nopass', 'display_name': ugettext_lazy('No encryption')}),
|
||||
weight=570,
|
||||
group=ugettext_lazy('Users'),
|
||||
subgroup=ugettext_lazy('PDF'))
|
||||
|
||||
|
||||
def create_builtin_groups_and_admin(**kwargs):
|
||||
|
@ -4,6 +4,7 @@ from urllib.parse import urlparse
|
||||
from django.core.urlresolvers import reverse
|
||||
from rest_framework.decorators import detail_route # noqa
|
||||
from rest_framework.decorators import list_route # noqa
|
||||
from rest_framework.metadata import SimpleMetadata # noqa
|
||||
from rest_framework.mixins import DestroyModelMixin, UpdateModelMixin # noqa
|
||||
from rest_framework.response import Response # noqa
|
||||
from rest_framework.routers import DefaultRouter
|
||||
|
@ -1,11 +1,11 @@
|
||||
from django import forms
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.dispatch import receiver
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from openslides.config.api import ConfigCollection, ConfigVariable, config
|
||||
from openslides.config.api import ConfigVariable, config
|
||||
from openslides.config.signals import config_signal
|
||||
from openslides.utils.rest_api import ValidationError
|
||||
from openslides.utils.test import TestCase
|
||||
|
||||
|
||||
@ -38,7 +38,51 @@ class ConfigViewSet(TestCase):
|
||||
reverse('config-detail', args=['test_var_ohhii4iavoh5Phoh5ahg']),
|
||||
{'value': 'test_value_string'})
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
self.assertEqual(response.data, {'detail': 'Enter a whole number.'})
|
||||
self.assertEqual(response.data, {'detail': "Wrong datatype. Expected <class 'int'>, got <class 'str'>."})
|
||||
|
||||
def test_update_good_choice(self):
|
||||
self.client = APIClient()
|
||||
self.client.login(username='admin', password='admin')
|
||||
response = self.client.put(
|
||||
reverse('config-detail', args=['test_var_wei0Rei9ahzooSohK1ph']),
|
||||
{'value': 'key_2_yahb2ain1aeZ1lea1Pei'})
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(config['test_var_wei0Rei9ahzooSohK1ph'], 'key_2_yahb2ain1aeZ1lea1Pei')
|
||||
|
||||
def test_update_bad_choice(self):
|
||||
self.client = APIClient()
|
||||
self.client.login(username='admin', password='admin')
|
||||
response = self.client.put(
|
||||
reverse('config-detail', args=['test_var_wei0Rei9ahzooSohK1ph']),
|
||||
{'value': 'test_value_bad_string'})
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
self.assertEqual(response.data, {'detail': 'Invalid input. Choice does not match.'})
|
||||
|
||||
def test_update_validator_ok(self):
|
||||
self.client = APIClient()
|
||||
self.client.login(username='admin', password='admin')
|
||||
response = self.client.put(
|
||||
reverse('config-detail', args=['test_var_Hi7Oje8Oith7goopeeng']),
|
||||
{'value': 'valid_string'})
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(config['test_var_Hi7Oje8Oith7goopeeng'], 'valid_string')
|
||||
|
||||
def test_update_validator_invalid(self):
|
||||
self.client = APIClient()
|
||||
self.client.login(username='admin', password='admin')
|
||||
response = self.client.put(
|
||||
reverse('config-detail', args=['test_var_Hi7Oje8Oith7goopeeng']),
|
||||
{'value': 'invalid_string'})
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
self.assertEqual(response.data, {'detail': 'Invalid input.'})
|
||||
|
||||
|
||||
def validator_for_testing(value):
|
||||
"""
|
||||
Validator for testing.
|
||||
"""
|
||||
if value == 'invalid_string':
|
||||
raise ValidationError({'detail': 'Invalid input.'})
|
||||
|
||||
|
||||
@receiver(config_signal, dispatch_uid='set_simple_config_view_integration_config_test')
|
||||
@ -47,14 +91,29 @@ def set_simple_config_view_integration_config_test(sender, **kwargs):
|
||||
Sets a simple config view with some config variables but without
|
||||
grouping.
|
||||
"""
|
||||
return ConfigCollection(
|
||||
title='Config vars for testing',
|
||||
url='test_url_ieXao5Wae5Duoy6Wohtu',
|
||||
variables=(ConfigVariable(name='test_var_aeW3Quahkah1phahCheo',
|
||||
default_value=None),
|
||||
ConfigVariable(name='test_var_Xeiizi7ooH8Thuk5aida',
|
||||
default_value='',
|
||||
form_field=forms.CharField()),
|
||||
ConfigVariable(name='test_var_ohhii4iavoh5Phoh5ahg',
|
||||
yield ConfigVariable(
|
||||
name='test_var_aeW3Quahkah1phahCheo',
|
||||
default_value=None,
|
||||
label='test_label_aeNahsheu8phahk8taYo')
|
||||
|
||||
yield ConfigVariable(
|
||||
name='test_var_Xeiizi7ooH8Thuk5aida',
|
||||
default_value='')
|
||||
|
||||
yield ConfigVariable(
|
||||
name='test_var_ohhii4iavoh5Phoh5ahg',
|
||||
default_value=0,
|
||||
form_field=forms.IntegerField())))
|
||||
input_type='integer')
|
||||
|
||||
yield ConfigVariable(
|
||||
name='test_var_wei0Rei9ahzooSohK1ph',
|
||||
default_value='key_1_Queit2juchoocos2Vugh',
|
||||
input_type='choice',
|
||||
choices=(
|
||||
{'value': 'key_1_Queit2juchoocos2Vugh', 'display_name': 'label_1_Queit2juchoocos2Vugh'},
|
||||
{'value': 'key_2_yahb2ain1aeZ1lea1Pei', 'display_name': 'label_2_yahb2ain1aeZ1lea1Pei'}))
|
||||
|
||||
yield ConfigVariable(
|
||||
name='test_var_Hi7Oje8Oith7goopeeng',
|
||||
default_value='',
|
||||
validators=(validator_for_testing,))
|
||||
|
@ -1,19 +1,8 @@
|
||||
from django import forms
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.dispatch import receiver
|
||||
from django.test.client import Client
|
||||
|
||||
from openslides.config.api import (
|
||||
ConfigCollection,
|
||||
ConfigGroup,
|
||||
ConfigGroupedCollection,
|
||||
ConfigVariable,
|
||||
config,
|
||||
)
|
||||
from openslides.config.api import ConfigVariable, config
|
||||
from openslides.config.exceptions import ConfigError, ConfigNotFound
|
||||
from openslides.config.signals import config_signal
|
||||
from openslides.users.models import User
|
||||
from openslides.utils.test import TestCase
|
||||
|
||||
|
||||
@ -86,79 +75,47 @@ class HandleConfigTest(TestCase):
|
||||
value='new_string_kbmbnfhdgibkdjshg452bc')
|
||||
self.assertEqual(config['var_with_callback_ghvnfjd5768gdfkwg0hm2'], 'new_string_kbmbnfhdgibkdjshg452bc')
|
||||
|
||||
def test_get_default(self):
|
||||
"""
|
||||
Tests the methode 'default'.
|
||||
"""
|
||||
self.assertEqual(config.get_default('string_var'), 'default_string_rien4ooCZieng6ah')
|
||||
self.assertRaisesMessage(
|
||||
ConfigNotFound,
|
||||
'The config variable unknown_var was not found.',
|
||||
config.get_default,
|
||||
'unknown_var')
|
||||
|
||||
|
||||
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_user('config_test_manager', '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_views_abstract(self):
|
||||
config_collection_dict = {}
|
||||
for signal_receiver, config_collection in config_signal.send(sender=self):
|
||||
config_collection_dict[signal_receiver.__name__] = config_collection
|
||||
self.assertGreater(config_collection_dict['set_grouped_config_view'].weight, config_collection_dict['set_simple_config_view'].weight)
|
||||
|
||||
|
||||
@receiver(config_signal, dispatch_uid='set_grouped_config_view_for_testing')
|
||||
def set_grouped_config_view(sender, **kwargs):
|
||||
"""
|
||||
Sets a grouped config collection view which can be reached under the url
|
||||
'/config/testgroupedpage1/'. There are some variables, one variable
|
||||
Sets a grouped config collection. 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.
|
||||
one with an integer as default value, one with choices and one hidden
|
||||
variable. These variables are grouped in two subgroups.
|
||||
"""
|
||||
string_var = ConfigVariable(
|
||||
yield ConfigVariable(
|
||||
name='string_var',
|
||||
default_value='default_string_rien4ooCZieng6ah',
|
||||
form_field=forms.CharField())
|
||||
bool_var = ConfigVariable(
|
||||
group='Config vars for testing 1',
|
||||
subgroup='Group 1 aiYeix2mCieQuae3')
|
||||
yield ConfigVariable(
|
||||
name='bool_var',
|
||||
default_value=True,
|
||||
form_field=forms.BooleanField(required=False))
|
||||
integer_var = ConfigVariable(
|
||||
input_type='boolean',
|
||||
group='Config vars for testing 1',
|
||||
subgroup='Group 1 aiYeix2mCieQuae3')
|
||||
yield 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))
|
||||
input_type='integer',
|
||||
group='Config vars for testing 1',
|
||||
subgroup='Group 1 aiYeix2mCieQuae3')
|
||||
|
||||
hidden_var = ConfigVariable(
|
||||
yield ConfigVariable(
|
||||
name='hidden_var',
|
||||
default_value='hidden_value')
|
||||
choices_var = ConfigVariable(
|
||||
default_value='hidden_value',
|
||||
group='Config vars for testing 1',
|
||||
subgroup='Group 2 Toongai7ahyahy7B')
|
||||
yield 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 ConfigGroupedCollection(
|
||||
title='Config vars for testing 1',
|
||||
url='testgroupedpage1',
|
||||
weight=10000,
|
||||
groups=(group_1, group_2),
|
||||
extra_context={'extra_stylefiles': ['styles/test-config-sjNN56dFGDrg2.css'],
|
||||
'extra_javascript': ['javascript/test-config-djg4dFGVslk4209f.js']})
|
||||
input_type='choice',
|
||||
choices=(
|
||||
{'value': '1', 'display_name': 'Choice One Ughoch4ocoche6Ee'},
|
||||
{'value': '2', 'display_name': 'Choice Two Vahnoh5yalohv5Eb'}),
|
||||
group='Config vars for testing 1',
|
||||
subgroup='Group 2 Toongai7ahyahy7B')
|
||||
|
||||
|
||||
@receiver(config_signal, dispatch_uid='set_simple_config_view_for_testing')
|
||||
@ -167,12 +124,9 @@ def set_simple_config_view(sender, **kwargs):
|
||||
Sets a simple config view with some config variables but without
|
||||
grouping.
|
||||
"""
|
||||
return ConfigCollection(
|
||||
title='Config vars for testing 2',
|
||||
url='testsimplepage1',
|
||||
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)))
|
||||
yield ConfigVariable(name='additional_config_var', default_value='BaeB0ahcMae3feem')
|
||||
yield ConfigVariable(name='additional_config_var_2', default_value='')
|
||||
yield ConfigVariable(name='none_config_var', default_value=None)
|
||||
|
||||
|
||||
# Do not connect to the signal now but later inside the test.
|
||||
@ -180,29 +134,20 @@ def set_simple_config_view_multiple_vars(sender, **kwargs):
|
||||
"""
|
||||
Sets a bad config view with some multiple config vars.
|
||||
"""
|
||||
return ConfigCollection(
|
||||
title='Config vars for testing 3',
|
||||
url='testsimplepage2',
|
||||
variables=(ConfigVariable(name='multiple_config_var', default_value='foobar1'),
|
||||
ConfigVariable(name='multiple_config_var', default_value='foobar2')))
|
||||
yield ConfigVariable(name='multiple_config_var', default_value='foobar1')
|
||||
yield ConfigVariable(name='multiple_config_var', default_value='foobar2')
|
||||
|
||||
|
||||
@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',
|
||||
variables=(ConfigVariable(name='hidden_config_var_2', default_value=''),))
|
||||
yield ConfigVariable(name='hidden_config_var_2', default_value='')
|
||||
|
||||
|
||||
@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 ConfigCollection(
|
||||
title='Hvndfhsbgkridfgdfg',
|
||||
url='testsimplepage4',
|
||||
variables=(ConfigVariable(
|
||||
yield ConfigVariable(
|
||||
name='var_with_callback_ghvnfjd5768gdfkwg0hm2',
|
||||
default_value='',
|
||||
on_change=callback),))
|
||||
on_change=callback)
|
||||
|
Loading…
Reference in New Issue
Block a user