Refactored config API.
Removed form_field attributes. Added extra fields for HTML rendering like label and help text. Added fields for sorting and grouping. Removed old collection system. Added config groups to config view via OPTIONS requests. Regrouped all variables. Added validation. Changed internal handling.
This commit is contained in:
parent
4506d787ee
commit
c5fbe2e9ee
@ -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