Merge pull request #2175 from ostcar/config

Redesign of the config variables.
This commit is contained in:
Oskar Hahn 2016-06-04 01:12:50 +02:00
commit f2570551a1
24 changed files with 658 additions and 657 deletions

View File

@ -8,6 +8,9 @@ Version 2.0.1 (unreleased)
========================== ==========================
[https://github.com/OpenSlides/OpenSlides/milestones/2.0.1] [https://github.com/OpenSlides/OpenSlides/milestones/2.0.1]
Other:
- Remove config cache to support multiple threads or processes.
Version 2.0 (2016-04-18) Version 2.0 (2016-04-18)
======================== ========================

View File

@ -15,16 +15,18 @@ class AgendaAppConfig(AppConfig):
# Import all required stuff. # Import all required stuff.
from django.db.models.signals import pre_delete, post_save from django.db.models.signals import pre_delete, post_save
from openslides.core.signals import config_signal from openslides.core.config import config
from openslides.utils.rest_api import router from openslides.utils.rest_api import router
from .config_variables import get_config_variables
from .signals import ( from .signals import (
setup_agenda_config,
listen_to_related_object_post_delete, listen_to_related_object_post_delete,
listen_to_related_object_post_save) listen_to_related_object_post_save)
from .views import ItemViewSet from .views import ItemViewSet
# Define config varialbes
config.update_config_varialbes(get_config_variables())
# Connect signals. # Connect signals.
config_signal.connect(setup_agenda_config, dispatch_uid='setup_agenda_config')
post_save.connect( post_save.connect(
listen_to_related_object_post_save, listen_to_related_object_post_save,
dispatch_uid='listen_to_related_object_post_save') dispatch_uid='listen_to_related_object_post_save')

View File

@ -0,0 +1,88 @@
from datetime import datetime
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
from openslides.core.config import ConfigVariable
def validate_start_time(value):
try:
datetime.strptime(value, '%d.%m.%Y %H:%M')
except ValueError:
raise DjangoValidationError(_('Invalid input.'))
def get_config_variables():
"""
Generator which yields all config variables of this app.
It has to be evaluated during app loading (see apps.py).
"""
yield ConfigVariable(
name='agenda_number_prefix',
default_value='',
label=ugettext_lazy('Numbering prefix for agenda items'),
help_text=ugettext_lazy('This prefix will be set if you run the automatic agenda numbering.'),
weight=210,
group=ugettext_lazy('Agenda'),
subgroup=ugettext_lazy('General'),
validators=(MaxLengthValidator(20),))
yield ConfigVariable(
name='agenda_numeral_system',
default_value='arabic',
input_type='choice',
label=ugettext_lazy('Numeral system for agenda items'),
choices=(
{'value': 'arabic', 'display_name': ugettext_lazy('Arabic')},
{'value': 'roman', 'display_name': ugettext_lazy('Roman')}),
weight=215,
group=ugettext_lazy('Agenda'),
subgroup=ugettext_lazy('General'))
# TODO: Use an input type with generic datetime support.
yield ConfigVariable(
name='agenda_start_event_date_time',
default_value='',
label=ugettext_lazy('Begin of event'),
help_text=ugettext_lazy('Input format: DD.MM.YYYY HH:MM'),
weight=220,
group=ugettext_lazy('Agenda'),
subgroup=ugettext_lazy('General'),
validators=(validate_start_time,))
# List of speakers
yield ConfigVariable(
name='agenda_show_last_speakers',
default_value=1,
input_type='integer',
label=ugettext_lazy('Number of last speakers to be shown on the projector'),
weight=230,
group=ugettext_lazy('Agenda'),
subgroup=ugettext_lazy('List of speakers'),
validators=(MinValueValidator(0),))
yield ConfigVariable(
name='agenda_countdown_warning_time',
default_value=0,
input_type='integer',
label=ugettext_lazy('Show orange countdown in the last x seconds of speaking time'),
help_text=ugettext_lazy('Enter duration in seconds. Choose 0 to disable warning color.'),
weight=235,
group=ugettext_lazy('Agenda'),
subgroup=ugettext_lazy('List of speakers'),
validators=(MinValueValidator(0),))
yield ConfigVariable(
name='agenda_couple_countdown_and_speakers',
default_value=False,
input_type='boolean',
label=ugettext_lazy('Couple countdown with the list of speakers'),
help_text=ugettext_lazy('[Begin speech] starts the countdown, [End speech] stops the countdown.'),
weight=240,
group=ugettext_lazy('Agenda'),
subgroup=ugettext_lazy('List of speakers'))

View File

@ -1,97 +1,10 @@
from datetime import datetime
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
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
from openslides.core.config import ConfigVariable
from openslides.utils.autoupdate import inform_changed_data from openslides.utils.autoupdate import inform_changed_data
from .models import Item from .models import Item
def validate_start_time(value):
try:
datetime.strptime(value, '%d.%m.%Y %H:%M')
except ValueError:
raise DjangoValidationError(_('Invalid input.'))
def setup_agenda_config(sender, **kwargs):
"""
Receiver function to setup all agenda config variables.
This function connected to the signal openslides.core.signals.config_signal
during app loading.
"""
yield ConfigVariable(
name='agenda_number_prefix',
default_value='',
label=ugettext_lazy('Numbering prefix for agenda items'),
help_text=ugettext_lazy('This prefix will be set if you run the automatic agenda numbering.'),
weight=210,
group=ugettext_lazy('Agenda'),
subgroup=ugettext_lazy('General'),
validators=(MaxLengthValidator(20),))
yield ConfigVariable(
name='agenda_numeral_system',
default_value='arabic',
input_type='choice',
label=ugettext_lazy('Numeral system for agenda items'),
choices=(
{'value': 'arabic', 'display_name': ugettext_lazy('Arabic')},
{'value': 'roman', 'display_name': ugettext_lazy('Roman')}),
weight=215,
group=ugettext_lazy('Agenda'),
subgroup=ugettext_lazy('General'))
# TODO: Use an input type with generic datetime support.
yield ConfigVariable(
name='agenda_start_event_date_time',
default_value='',
label=ugettext_lazy('Begin of event'),
help_text=ugettext_lazy('Input format: DD.MM.YYYY HH:MM'),
weight=220,
group=ugettext_lazy('Agenda'),
subgroup=ugettext_lazy('General'),
validators=(validate_start_time,))
# List of speakers
yield ConfigVariable(
name='agenda_show_last_speakers',
default_value=1,
input_type='integer',
label=ugettext_lazy('Number of last speakers to be shown on the projector'),
weight=230,
group=ugettext_lazy('Agenda'),
subgroup=ugettext_lazy('List of speakers'),
validators=(MinValueValidator(0),))
yield ConfigVariable(
name='agenda_countdown_warning_time',
default_value=0,
input_type='integer',
label=ugettext_lazy('Show orange countdown in the last x seconds of speaking time'),
help_text=ugettext_lazy('Enter duration in seconds. Choose 0 to disable warning color.'),
weight=235,
group=ugettext_lazy('Agenda'),
subgroup=ugettext_lazy('List of speakers'),
validators=(MinValueValidator(0),))
yield ConfigVariable(
name='agenda_couple_countdown_and_speakers',
default_value=False,
input_type='boolean',
label=ugettext_lazy('Couple countdown with the list of speakers'),
help_text=ugettext_lazy('[Begin speech] starts the countdown, [End speech] stops the countdown.'),
weight=240,
group=ugettext_lazy('Agenda'),
subgroup=ugettext_lazy('List of speakers'))
def listen_to_related_object_post_save(sender, instance, created, **kwargs): def listen_to_related_object_post_save(sender, instance, created, **kwargs):
""" """
Receiver function to create agenda items. It is connected to the signal Receiver function to create agenda items. It is connected to the signal

View File

@ -14,13 +14,13 @@ class AssignmentsAppConfig(AppConfig):
from . import projector # noqa from . import projector # noqa
# Import all required stuff. # Import all required stuff.
from openslides.core.signals import config_signal from openslides.core.config import config
from openslides.utils.rest_api import router from openslides.utils.rest_api import router
from .signals import setup_assignment_config from .config_variables import get_config_variables
from .views import AssignmentViewSet, AssignmentPollViewSet from .views import AssignmentViewSet, AssignmentPollViewSet
# Connect signals. # Define config variables
config_signal.connect(setup_assignment_config, dispatch_uid='setup_assignment_config') config.update_config_varialbes(get_config_variables())
# Register viewsets. # Register viewsets.
router.register(self.get_model('Assignment').get_collection_string(), AssignmentViewSet) router.register(self.get_model('Assignment').get_collection_string(), AssignmentViewSet)

View File

@ -6,15 +6,14 @@ from openslides.core.config import ConfigVariable
from openslides.poll.models import PERCENT_BASE_CHOICES from openslides.poll.models import PERCENT_BASE_CHOICES
def setup_assignment_config(sender, **kwargs): def get_config_variables():
""" """
Receiver function to setup all assignment config variables. They are Generator which yields all config variables of this app.
grouped in 'Ballot and ballot papers' and 'PDF'. This function is
connected to the signal openslides.core.signals.config_signal during They are grouped in 'Ballot and ballot papers' and 'PDF'. The generator has
app loading. to be evaluated during app loading (see apps.py).
""" """
# Ballot and ballot papers # Ballot and ballot papers
yield ConfigVariable( yield ConfigVariable(
name='assignments_poll_vote_values', name='assignments_poll_vote_values',
default_value='auto', default_value='auto',

View File

@ -15,11 +15,13 @@ class CoreAppConfig(AppConfig):
# Import all required stuff. # Import all required stuff.
from django.db.models import signals from django.db.models import signals
from openslides.core.signals import config_signal, post_permission_creation from openslides.core.config import config
from openslides.core.signals import post_permission_creation
from openslides.utils.autoupdate import inform_changed_data_receiver, inform_deleted_data_receiver from openslides.utils.autoupdate import inform_changed_data_receiver, inform_deleted_data_receiver
from openslides.utils.rest_api import router from openslides.utils.rest_api import router
from openslides.utils.search import index_add_instance, index_del_instance from openslides.utils.search import index_add_instance, index_del_instance
from .signals import delete_django_app_permissions, setup_general_config from .config_variables import get_config_variables
from .signals import delete_django_app_permissions
from .views import ( from .views import (
ChatMessageViewSet, ChatMessageViewSet,
ConfigViewSet, ConfigViewSet,
@ -28,10 +30,10 @@ class CoreAppConfig(AppConfig):
TagViewSet, TagViewSet,
) )
# Define config variables
config.update_config_varialbes(get_config_variables())
# Connect signals. # Connect signals.
config_signal.connect(
setup_general_config,
dispatch_uid='setup_general_config')
post_permission_creation.connect( post_permission_creation.connect(
delete_django_app_permissions, delete_django_app_permissions,
dispatch_uid='delete_django_app_permissions') dispatch_uid='delete_django_app_permissions')

View File

@ -19,33 +19,35 @@ class ConfigHandler:
object. To get a config variable use x = config[...], to set it use object. To get a config variable use x = config[...], to set it use
config[...] = x. config[...] = x.
""" """
def __init__(self):
# Dict, that keeps all ConfigVariable objects. Has to be set at statup.
# See the run method in openslides.core.apps.
self.config_variables = {}
def __getitem__(self, key): def __getitem__(self, key):
""" """
Returns the value of the config variable. Builds the cache if it Returns the value of the config variable. Returns the default value, if
does not exist. not value exists in the database.
""" """
try: try:
return self._cache[key] default_value = self.config_variables[key].default_value
except KeyError: except KeyError:
raise ConfigNotFound(_('The config variable %s was not found.') % key) raise ConfigNotFound(_('The config variable {} was not found.').format(key))
except AttributeError:
self.setup_cache()
return self[key]
def setup_cache(self): try:
""" db_value = ConfigStore.objects.get(key=key)
Creates a cache of all config variables with their current value. except ConfigStore.DoesNotExist:
""" return default_value
self._cache = {} return db_value.value
for key, config_variable in self.get_config_variables().items():
self._cache[key] = config_variable.default_value
for config_object in ConfigStore.objects.all():
self._cache[config_object.key] = config_object.value
def __contains__(self, key): def __contains__(self, key):
"""
Returns True, if the config varialbe exists.
"""
try: try:
config[key] self.config_variables[key]
except ConfigNotFound: except KeyError:
return False return False
else: else:
return True return True
@ -56,9 +58,9 @@ class ConfigHandler:
""" """
# Check if the variable is defined. # Check if the variable is defined.
try: try:
config_variable = config.get_config_variables()[key] config_variable = self.config_variables[key]
except KeyError: except KeyError:
raise ConfigNotFound(_('The config variable %s was not found.') % key) raise ConfigNotFound(_('The config variable {} was not found.').format(key))
# Validate datatype and run validators. # Validate datatype and run validators.
expected_type = INPUT_TYPE_MAPPING[config_variable.input_type] expected_type = INPUT_TYPE_MAPPING[config_variable.input_type]
@ -69,7 +71,14 @@ class ConfigHandler:
except ValueError: except ValueError:
raise ConfigError(_('Wrong datatype. Expected %(expected_type)s, got %(got_type)s.') % { raise ConfigError(_('Wrong datatype. Expected %(expected_type)s, got %(got_type)s.') % {
'expected_type': expected_type, 'got_type': type(value)}) 'expected_type': expected_type, 'got_type': type(value)})
if config_variable.input_type == 'choice' and value not in map(lambda choice: choice['value'], config_variable.choices):
if config_variable.input_type == 'choice':
# Choices can be a callable. In this case call it at this place
if callable(config_variable.choices):
choices = config_variable.choices()
else:
choices = config_variable.choices
if value not in map(lambda choice: choice['value'], choices):
raise ConfigError(_('Invalid input. Choice does not match.')) raise ConfigError(_('Invalid input. Choice does not match.'))
for validator in config_variable.validators: for validator in config_variable.validators:
try: try:
@ -78,50 +87,45 @@ class ConfigHandler:
raise ConfigError(e.messages[0]) raise ConfigError(e.messages[0])
# Save the new value to the database. # Save the new value to the database.
config_store, created = ConfigStore.objects.get_or_create(key=key, defaults={'value': value}) ConfigStore.objects.update_or_create(key=key, defaults={'value': value})
if not created:
config_store.value = value
config_store.save()
# Update cache.
if hasattr(self, '_cache'):
self._cache[key] = value
# Call on_change callback. # Call on_change callback.
if config_variable.on_change: if config_variable.on_change:
config_variable.on_change() config_variable.on_change()
def update_config_varialbes(self, items):
"""
Updates the config_variables dict.
items has to be an iterator over ConfigVariable objects.
"""
new_items = dict((variable.name, variable) for variable in items)
# Check that all ConfigVariables are unique. So no key from items can
# be in already in self.config_variables
for key in new_items.keys():
if key in self.config_variables:
raise ConfigError(_('Too many values for config variable {} found.').format(key))
self.config_variables.update(new_items)
def items(self): def items(self):
""" """
Returns key-value pairs of all config variables. Iterates over key-value pairs of all config variables.
""" """
if not hasattr(self, '_cache'): # Create a dict with the default values of each ConfigVariable
self.setup_cache() config_items = dict((key, variable.default_value) for key, variable in self.config_variables.items())
return self._cache.items()
def get_config_variables(self): # Update the dict with all values, which are in the db
""" for db_value in ConfigStore.objects.all():
Returns a dictionary with all ConfigVariable instances of all config_items[db_value.key] = db_value.value
signal receivers. The key is the name of the config variable. return config_items.items()
"""
# config_signal can not be imported at global space, because
# core.signals imports this file
from .signals import config_signal
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): def get_all_translatable(self):
""" """
Generator to get all config variables as strings when their values are Generator to get all config variables as strings when their values are
intended to be translated. intended to be translated.
""" """
for config_variable in self.get_config_variables().values(): for config_variable in self.config_variables.values():
if config_variable.translatable: if config_variable.translatable:
yield config_variable.name yield config_variable.name

View File

@ -0,0 +1,160 @@
from django.core.validators import MaxLengthValidator
from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy
from openslides.core.config import ConfigVariable
def get_config_variables():
"""
Generator which yields all config variables of this app.
There are two main groups: 'General' and 'Projector'. The group 'General'
has subgroups. The generator has to be evaluated during app loading
(see apps.py).
"""
yield ConfigVariable(
name='general_event_name',
default_value='OpenSlides',
label=ugettext_lazy('Event name'),
weight=110,
group=ugettext_lazy('General'),
subgroup=ugettext_lazy('Event'),
validators=(MaxLengthValidator(50),))
yield ConfigVariable(
name='general_event_description',
default_value=_('Presentation and assembly system'),
label=ugettext_lazy('Short description of event'),
weight=115,
group=ugettext_lazy('General'),
subgroup=ugettext_lazy('Event'),
validators=(MaxLengthValidator(100),),
translatable=True)
yield ConfigVariable(
name='general_event_date',
default_value='',
label=ugettext_lazy('Event date'),
weight=120,
group=ugettext_lazy('General'),
subgroup=ugettext_lazy('Event'))
yield ConfigVariable(
name='general_event_location',
default_value='',
label=ugettext_lazy('Event location'),
weight=125,
group=ugettext_lazy('General'),
subgroup=ugettext_lazy('Event'))
yield ConfigVariable(
name='general_event_organizer',
default_value='',
label=ugettext_lazy('Event organizer'),
weight=130,
group=ugettext_lazy('General'),
subgroup=ugettext_lazy('Event'))
yield ConfigVariable(
name='general_event_legal_notice',
default_value=_(
'<a href="http://www.openslides.org">OpenSlides</a> is a free web based '
'presentation and assembly system for visualizing and controlling agenda, '
'motions and elections of an assembly.'),
input_type='text',
label=ugettext_lazy('Legal notice'),
weight=132,
group=ugettext_lazy('General'),
subgroup=ugettext_lazy('Event'),
translatable=True)
yield ConfigVariable(
name='general_event_welcome_title',
default_value=_('Welcome to OpenSlides'),
label=ugettext_lazy('Front page title'),
weight=134,
group=ugettext_lazy('General'),
subgroup=ugettext_lazy('Event'),
translatable=True)
yield ConfigVariable(
name='general_event_welcome_text',
default_value=_('[Space for your welcome text.]'),
input_type='text',
label=ugettext_lazy('Front page text'),
weight=136,
group=ugettext_lazy('General'),
subgroup=ugettext_lazy('Event'),
translatable=True)
# General System
yield ConfigVariable(
name='general_system_enable_anonymous',
default_value=False,
input_type='boolean',
label=ugettext_lazy('Allow access for anonymous guest users'),
weight=138,
group=ugettext_lazy('General'),
subgroup=ugettext_lazy('System'))
yield ConfigVariable(
name='general_login_info_text',
default_value='',
label=ugettext_lazy('Show this text on the login page.'),
weight=140,
group=ugettext_lazy('General'),
subgroup=ugettext_lazy('System'))
# Projector
yield ConfigVariable(
name='projector_enable_logo',
default_value=True,
input_type='boolean',
label=ugettext_lazy('Show logo on projector'),
help_text=ugettext_lazy(
'You can replace the logo. Just copy a file to '
'"static/img/logo-projector.png" in your OpenSlides data path.'),
weight=150,
group=ugettext_lazy('Projector'))
yield ConfigVariable(
name='projector_enable_title',
default_value=True,
input_type='boolean',
label=ugettext_lazy('Show title and description of event on projector'),
weight=155,
group=ugettext_lazy('Projector'))
yield ConfigVariable(
name='projector_header_backgroundcolor',
default_value='#317796',
input_type='colorpicker',
label=ugettext_lazy('Background color of projector header and footer'),
weight=160,
group=ugettext_lazy('Projector'))
yield ConfigVariable(
name='projector_header_fontcolor',
default_value='#F5F5F5',
input_type='colorpicker',
label=ugettext_lazy('Font color of projector header and footer'),
weight=165,
group=ugettext_lazy('Projector'))
yield ConfigVariable(
name='projector_h1_fontcolor',
default_value='#317796',
input_type='colorpicker',
label=ugettext_lazy('Font color of projector headline'),
weight=170,
group=ugettext_lazy('Projector'))
yield ConfigVariable(
name='projector_default_countdown',
default_value=60,
label=ugettext_lazy('Default countdown'),
weight=185,
group=ugettext_lazy('Projector'))

View File

@ -1,9 +0,0 @@
from .config import config
class ConfigCacheMiddleware(object):
"""
Middleware to refresh the config cache before processing any view.
"""
def process_request(self, request):
config.setup_cache()

View File

@ -1,12 +1,7 @@
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.validators import MaxLengthValidator
from django.db.models import Q from django.db.models import Q
from django.dispatch import Signal from django.dispatch import Signal
from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy
from openslides.core.config import ConfigVariable
# This signal is sent when the migrate command is done. That means it is sent # 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 # after post_migrate sending and creating all Permission objects. Don't use it
@ -25,163 +20,3 @@ def delete_django_app_permissions(sender, **kwargs):
Q(app_label='sessions')) Q(app_label='sessions'))
for permission in Permission.objects.filter(content_type__in=contenttypes): for permission in Permission.objects.filter(content_type__in=contenttypes):
permission.delete() permission.delete()
def setup_general_config(sender, **kwargs):
"""
Receiver function to setup general config variables for OpenSlides.
There are two main groups: 'General' and 'Projector'. The group
'General' has subgroups. This function is connected to the signal
openslides.core.signals.config_signal during app loading.
"""
# General Event
yield ConfigVariable(
name='general_event_name',
default_value='OpenSlides',
label=ugettext_lazy('Event name'),
weight=110,
group=ugettext_lazy('General'),
subgroup=ugettext_lazy('Event'),
validators=(MaxLengthValidator(50),))
yield ConfigVariable(
name='general_event_description',
default_value=_('Presentation and assembly system'),
label=ugettext_lazy('Short description of event'),
weight=115,
group=ugettext_lazy('General'),
subgroup=ugettext_lazy('Event'),
validators=(MaxLengthValidator(100),),
translatable=True)
yield ConfigVariable(
name='general_event_date',
default_value='',
label=ugettext_lazy('Event date'),
weight=120,
group=ugettext_lazy('General'),
subgroup=ugettext_lazy('Event'))
yield ConfigVariable(
name='general_event_location',
default_value='',
label=ugettext_lazy('Event location'),
weight=125,
group=ugettext_lazy('General'),
subgroup=ugettext_lazy('Event'))
yield ConfigVariable(
name='general_event_organizer',
default_value='',
label=ugettext_lazy('Event organizer'),
weight=130,
group=ugettext_lazy('General'),
subgroup=ugettext_lazy('Event'))
yield ConfigVariable(
name='general_event_legal_notice',
default_value=_(
'<a href="http://www.openslides.org">OpenSlides</a> is a free web based '
'presentation and assembly system for visualizing and controlling agenda, '
'motions and elections of an assembly.'),
input_type='text',
label=ugettext_lazy('Legal notice'),
weight=132,
group=ugettext_lazy('General'),
subgroup=ugettext_lazy('Event'),
translatable=True)
yield ConfigVariable(
name='general_event_welcome_title',
default_value=_('Welcome to OpenSlides'),
label=ugettext_lazy('Front page title'),
weight=134,
group=ugettext_lazy('General'),
subgroup=ugettext_lazy('Event'),
translatable=True)
yield ConfigVariable(
name='general_event_welcome_text',
default_value=_('[Space for your welcome text.]'),
input_type='text',
label=ugettext_lazy('Front page text'),
weight=136,
group=ugettext_lazy('General'),
subgroup=ugettext_lazy('Event'),
translatable=True)
# General System
yield ConfigVariable(
name='general_system_enable_anonymous',
default_value=False,
input_type='boolean',
label=ugettext_lazy('Allow access for anonymous guest users'),
weight=138,
group=ugettext_lazy('General'),
subgroup=ugettext_lazy('System'))
yield ConfigVariable(
name='general_login_info_text',
default_value='',
label=ugettext_lazy('Show this text on the login page.'),
weight=140,
group=ugettext_lazy('General'),
subgroup=ugettext_lazy('System'))
# Projector
yield ConfigVariable(
name='projector_enable_logo',
default_value=True,
input_type='boolean',
label=ugettext_lazy('Show logo on projector'),
help_text=ugettext_lazy(
'You can replace the logo. Just copy a file to '
'"static/img/logo-projector.png" in your OpenSlides data path.'),
weight=150,
group=ugettext_lazy('Projector'))
yield ConfigVariable(
name='projector_enable_title',
default_value=True,
input_type='boolean',
label=ugettext_lazy('Show title and description of event on projector'),
weight=155,
group=ugettext_lazy('Projector'))
yield ConfigVariable(
name='projector_header_backgroundcolor',
default_value='#317796',
input_type='colorpicker',
label=ugettext_lazy('Background color of projector header and footer'),
weight=160,
group=ugettext_lazy('Projector'))
yield ConfigVariable(
name='projector_header_fontcolor',
default_value='#F5F5F5',
input_type='colorpicker',
label=ugettext_lazy('Font color of projector header and footer'),
weight=165,
group=ugettext_lazy('Projector'))
yield ConfigVariable(
name='projector_h1_fontcolor',
default_value='#317796',
input_type='colorpicker',
label=ugettext_lazy('Font color of projector headline'),
weight=170,
group=ugettext_lazy('Projector'))
yield ConfigVariable(
name='projector_default_countdown',
default_value=60,
label=ugettext_lazy('Default countdown'),
weight=185,
group=ugettext_lazy('Projector'))
config_signal = Signal(providing_args=[])
"""Signal to get all config tabs from all apps."""

View File

@ -415,12 +415,9 @@ class ConfigMetadata(SimpleMetadata):
Custom metadata class to add config info to responses on OPTIONS requests. Custom metadata class to add config info to responses on OPTIONS requests.
""" """
def determine_metadata(self, request, view): def determine_metadata(self, request, view):
# Sort config variables by weight.
config_variables = sorted(config.get_config_variables().values(), key=attrgetter('weight'))
# Build tree. # Build tree.
config_groups = [] config_groups = []
for config_variable in config_variables: for config_variable in sorted(config.config_variables.values(), key=attrgetter('weight')):
if config_variable.is_hidden(): if config_variable.is_hidden():
# Skip hidden config variables. Do not even check groups and subgroups. # Skip hidden config variables. Do not even check groups and subgroups.
continue continue

View File

@ -60,7 +60,6 @@ MIDDLEWARE_CLASSES = (
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
'openslides.users.auth.AuthenticationMiddleware', 'openslides.users.auth.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'openslides.core.middleware.ConfigCacheMiddleware',
) )
ROOT_URLCONF = 'openslides.urls' ROOT_URLCONF = 'openslides.urls'

View File

@ -15,13 +15,16 @@ class MotionsAppConfig(AppConfig):
from . import projector # noqa from . import projector # noqa
# Import all required stuff. # Import all required stuff.
from openslides.core.signals import config_signal from openslides.core.config import config
from openslides.utils.rest_api import router from openslides.utils.rest_api import router
from .signals import create_builtin_workflows, setup_motion_config from .config_variables import get_config_variables
from .signals import create_builtin_workflows
from .views import CategoryViewSet, MotionViewSet, MotionPollViewSet, WorkflowViewSet from .views import CategoryViewSet, MotionViewSet, MotionPollViewSet, WorkflowViewSet
# Define config varialbes
config.update_config_varialbes(get_config_variables())
# Connect signals. # Connect signals.
config_signal.connect(setup_motion_config, dispatch_uid='setup_motion_config')
post_migrate.connect(create_builtin_workflows, dispatch_uid='motion_create_builtin_workflows') post_migrate.connect(create_builtin_workflows, dispatch_uid='motion_create_builtin_workflows')
# Register viewsets. # Register viewsets.

View File

@ -0,0 +1,183 @@
from django.core.validators import MinValueValidator
from django.utils.translation import ugettext as _
from django.utils.translation import pgettext, ugettext_lazy
from openslides.core.config import ConfigVariable
from openslides.poll.models import PERCENT_BASE_CHOICES
from .models import Workflow
def get_workflow_choices():
"""
Returns a list of all workflows to be used as choices for the config variable
'motions_workflow'. Each list item contains the pk and the display name.
"""
return [{'value': str(workflow.pk), 'display_name': ugettext_lazy(workflow.name)}
for workflow in Workflow.objects.all()]
def get_config_variables():
"""
Generator which yields all config variables of this app.
They are grouped in 'General', 'Amendments', 'Supporters', 'Voting and ballot
papers' and 'PDF'. The generator has to be evaluated during app loading
(see apps.py).
"""
# General
yield ConfigVariable(
name='motions_workflow',
default_value='1',
input_type='choice',
label=ugettext_lazy('Workflow of new motions'),
choices=get_workflow_choices,
weight=310,
group=ugettext_lazy('Motions'),
subgroup=ugettext_lazy('General'))
yield ConfigVariable(
name='motions_identifier',
default_value='per_category',
input_type='choice',
label=ugettext_lazy('Identifier'),
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('Motions'),
subgroup=ugettext_lazy('General'))
yield ConfigVariable(
name='motions_preamble',
default_value=_('The assembly may decide,'),
label=ugettext_lazy('Motion preamble'),
weight=320,
group=ugettext_lazy('Motions'),
subgroup=ugettext_lazy('General'),
translatable=True)
yield ConfigVariable(
name='motions_stop_submitting',
default_value=False,
input_type='boolean',
label=ugettext_lazy('Stop submitting new motions by non-staff users'),
weight=325,
group=ugettext_lazy('Motions'),
subgroup=ugettext_lazy('General'))
yield ConfigVariable(
name='motions_allow_disable_versioning',
default_value=False,
input_type='boolean',
label=ugettext_lazy('Allow to disable versioning'),
weight=330,
group=ugettext_lazy('Motions'),
subgroup=ugettext_lazy('General'))
# Amendments
# Amendments currently not implemented. (TODO: Implement it like in OpenSlides 1.7.)
yield ConfigVariable(
name='motions_amendments_enabled',
default_value=False,
input_type='boolean',
label=ugettext_lazy('Activate amendments'),
hidden=True,
weight=335,
group=ugettext_lazy('Motions'),
subgroup=ugettext_lazy('Amendments'))
yield ConfigVariable(
name='motions_amendments_prefix',
default_value=pgettext('Prefix for the identifier for amendments', 'A'),
label=ugettext_lazy('Prefix for the identifier for amendments'),
hidden=True,
weight=340,
group=ugettext_lazy('Motions'),
subgroup=ugettext_lazy('Amendments'))
# Supporters
yield ConfigVariable(
name='motions_min_supporters',
default_value=0,
input_type='integer',
label=ugettext_lazy('Number of (minimum) required supporters for a motion'),
help_text=ugettext_lazy('Choose 0 to disable the supporting system.'),
weight=345,
group=ugettext_lazy('Motions'),
subgroup=ugettext_lazy('Supporters'),
validators=(MinValueValidator(0),))
yield ConfigVariable(
name='motions_remove_supporters',
default_value=False,
input_type='boolean',
label=ugettext_lazy('Remove all supporters of a motion if a submitter edits his motion in early state'),
weight=350,
group=ugettext_lazy('Motions'),
subgroup=ugettext_lazy('Supporters'))
# Voting and ballot papers
yield ConfigVariable(
name='motions_poll_100_percent_base',
default_value='WITHOUT_INVALID',
input_type='choice',
label=ugettext_lazy('The 100 % base of a voting result consists of'),
choices=PERCENT_BASE_CHOICES,
weight=355,
group=ugettext_lazy('Motions'),
subgroup=ugettext_lazy('Voting and ballot papers'))
yield ConfigVariable(
name='motions_pdf_ballot_papers_selection',
default_value='CUSTOM_NUMBER',
input_type='choice',
label=ugettext_lazy('Number of ballot papers (selection)'),
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('Motions'),
subgroup=ugettext_lazy('Voting and ballot papers'))
yield ConfigVariable(
name='motions_pdf_ballot_papers_number',
default_value=8,
input_type='integer',
label=ugettext_lazy('Custom number of ballot papers'),
weight=365,
group=ugettext_lazy('Motions'),
subgroup=ugettext_lazy('Voting and ballot papers'),
validators=(MinValueValidator(1),))
# PDF
yield ConfigVariable(
name='motions_pdf_title',
default_value=_('Motions'),
label=ugettext_lazy('Title for PDF document (all motions)'),
weight=370,
group=ugettext_lazy('Motions'),
subgroup=ugettext_lazy('PDF'),
translatable=True)
yield ConfigVariable(
name='motions_pdf_preamble',
default_value='',
label=ugettext_lazy('Preamble text for PDF document (all motions)'),
weight=375,
group=ugettext_lazy('Motions'),
subgroup=ugettext_lazy('PDF'))
yield ConfigVariable(
name='motions_pdf_paragraph_numbering',
default_value=False,
input_type='boolean',
label=ugettext_lazy('Show paragraph numbering (only in PDF)'),
weight=380,
group=ugettext_lazy('Motions'),
subgroup=ugettext_lazy('PDF'))

View File

@ -1,179 +1,8 @@
from django.core.validators import MinValueValidator from django.utils.translation import ugettext_noop
from django.utils.translation import ugettext as _
from django.utils.translation import pgettext, ugettext_lazy, ugettext_noop
from openslides.core.config import ConfigVariable
from openslides.poll.models import PERCENT_BASE_CHOICES
from .models import State, Workflow from .models import State, Workflow
def setup_motion_config(sender, **kwargs):
"""
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.core.signals.config_signal during app loading.
"""
# General
yield ConfigVariable(
name='motions_workflow',
default_value='1',
input_type='choice',
label=ugettext_lazy('Workflow of new motions'),
choices=({'value': str(workflow.pk), 'display_name': ugettext_lazy(workflow.name)} for workflow in Workflow.objects.all()),
weight=310,
group=ugettext_lazy('Motions'),
subgroup=ugettext_lazy('General'))
yield ConfigVariable(
name='motions_identifier',
default_value='per_category',
input_type='choice',
label=ugettext_lazy('Identifier'),
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('Motions'),
subgroup=ugettext_lazy('General'))
yield ConfigVariable(
name='motions_preamble',
default_value=_('The assembly may decide,'),
label=ugettext_lazy('Motion preamble'),
weight=320,
group=ugettext_lazy('Motions'),
subgroup=ugettext_lazy('General'),
translatable=True)
yield ConfigVariable(
name='motions_stop_submitting',
default_value=False,
input_type='boolean',
label=ugettext_lazy('Stop submitting new motions by non-staff users'),
weight=325,
group=ugettext_lazy('Motions'),
subgroup=ugettext_lazy('General'))
yield ConfigVariable(
name='motions_allow_disable_versioning',
default_value=False,
input_type='boolean',
label=ugettext_lazy('Allow to disable versioning'),
weight=330,
group=ugettext_lazy('Motions'),
subgroup=ugettext_lazy('General'))
# Amendments
# Amendments currently not implemented. (TODO: Implement it like in OpenSlides 1.7.)
yield ConfigVariable(
name='motions_amendments_enabled',
default_value=False,
input_type='boolean',
label=ugettext_lazy('Activate amendments'),
hidden=True,
weight=335,
group=ugettext_lazy('Motions'),
subgroup=ugettext_lazy('Amendments'))
yield ConfigVariable(
name='motions_amendments_prefix',
default_value=pgettext('Prefix for the identifier for amendments', 'A'),
label=ugettext_lazy('Prefix for the identifier for amendments'),
hidden=True,
weight=340,
group=ugettext_lazy('Motions'),
subgroup=ugettext_lazy('Amendments'))
# Supporters
yield ConfigVariable(
name='motions_min_supporters',
default_value=0,
input_type='integer',
label=ugettext_lazy('Number of (minimum) required supporters for a motion'),
help_text=ugettext_lazy('Choose 0 to disable the supporting system.'),
weight=345,
group=ugettext_lazy('Motions'),
subgroup=ugettext_lazy('Supporters'),
validators=(MinValueValidator(0),))
yield ConfigVariable(
name='motions_remove_supporters',
default_value=False,
input_type='boolean',
label=ugettext_lazy('Remove all supporters of a motion if a submitter edits his motion in early state'),
weight=350,
group=ugettext_lazy('Motions'),
subgroup=ugettext_lazy('Supporters'))
# Voting and ballot papers
yield ConfigVariable(
name='motions_poll_100_percent_base',
default_value='WITHOUT_INVALID',
input_type='choice',
label=ugettext_lazy('The 100 % base of a voting result consists of'),
choices=PERCENT_BASE_CHOICES,
weight=355,
group=ugettext_lazy('Motions'),
subgroup=ugettext_lazy('Voting and ballot papers'))
yield ConfigVariable(
name='motions_pdf_ballot_papers_selection',
default_value='CUSTOM_NUMBER',
input_type='choice',
label=ugettext_lazy('Number of ballot papers (selection)'),
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('Motions'),
subgroup=ugettext_lazy('Voting and ballot papers'))
yield ConfigVariable(
name='motions_pdf_ballot_papers_number',
default_value=8,
input_type='integer',
label=ugettext_lazy('Custom number of ballot papers'),
weight=365,
group=ugettext_lazy('Motions'),
subgroup=ugettext_lazy('Voting and ballot papers'),
validators=(MinValueValidator(1),))
# PDF
yield ConfigVariable(
name='motions_pdf_title',
default_value=_('Motions'),
label=ugettext_lazy('Title for PDF document (all motions)'),
weight=370,
group=ugettext_lazy('Motions'),
subgroup=ugettext_lazy('PDF'),
translatable=True)
yield ConfigVariable(
name='motions_pdf_preamble',
default_value='',
label=ugettext_lazy('Preamble text for PDF document (all motions)'),
weight=375,
group=ugettext_lazy('Motions'),
subgroup=ugettext_lazy('PDF'))
yield ConfigVariable(
name='motions_pdf_paragraph_numbering',
default_value=False,
input_type='boolean',
label=ugettext_lazy('Show paragraph numbering (only in PDF)'),
weight=380,
group=ugettext_lazy('Motions'),
subgroup=ugettext_lazy('PDF'))
def create_builtin_workflows(sender, **kwargs): def create_builtin_workflows(sender, **kwargs):
""" """
Receiver function to create a simple and a complex workflow. It is Receiver function to create a simple and a complex workflow. It is

View File

@ -14,15 +14,17 @@ class UsersAppConfig(AppConfig):
from . import projector # noqa from . import projector # noqa
# Import all required stuff. # Import all required stuff.
from ..core.signals import config_signal, post_permission_creation from ..core.config import config
from ..core.signals import post_permission_creation
from ..utils.rest_api import router from ..utils.rest_api import router
from .signals import create_builtin_groups_and_admin, setup_users_config from .config_variables import get_config_variables
from .signals import create_builtin_groups_and_admin
from .views import GroupViewSet, UserViewSet from .views import GroupViewSet, UserViewSet
# Define config variables
config.update_config_varialbes(get_config_variables())
# Connect signals. # Connect signals.
config_signal.connect(
setup_users_config,
dispatch_uid='setup_users_config')
post_permission_creation.connect( post_permission_creation.connect(
create_builtin_groups_and_admin, create_builtin_groups_and_admin,
dispatch_uid='create_builtin_groups_and_admin') dispatch_uid='create_builtin_groups_and_admin')

View File

@ -0,0 +1,86 @@
from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy
from openslides.core.config import ConfigVariable
def get_config_variables():
"""
Generator which yields all config variables of this app.
They are grouped in 'Sorting' and 'PDF'. The generator has to be evaluated
during app loading (see apps.py).
"""
# Sorting
yield ConfigVariable(
name='users_sort_users_by_first_name',
default_value=False,
input_type='boolean',
label=ugettext_lazy('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
yield ConfigVariable(
name='users_pdf_welcometitle',
default_value=_('Welcome to OpenSlides!'),
label=ugettext_lazy('Title for access data and welcome PDF'),
weight=520,
group=ugettext_lazy('Users'),
subgroup=ugettext_lazy('PDF'),
translatable=True)
yield ConfigVariable(
name='users_pdf_welcometext',
default_value=_('[Place for your welcome and help text.]'),
label=ugettext_lazy('Help text for access data and welcome PDF'),
weight=530,
group=ugettext_lazy('Users'),
subgroup=ugettext_lazy('PDF'),
translatable=True)
# TODO: Use Django's URLValidator here.
yield ConfigVariable(
name='users_pdf_url',
default_value='http://example.com:8000',
label=ugettext_lazy('System URL'),
help_text=ugettext_lazy('Used for QRCode in PDF of access data.'),
weight=540,
group=ugettext_lazy('Users'),
subgroup=ugettext_lazy('PDF'))
yield ConfigVariable(
name='users_pdf_wlan_ssid',
default_value='',
label=ugettext_lazy('WLAN name (SSID)'),
help_text=ugettext_lazy('Used for WLAN QRCode in PDF of access data.'),
weight=550,
group=ugettext_lazy('Users'),
subgroup=ugettext_lazy('PDF'))
yield ConfigVariable(
name='users_pdf_wlan_password',
default_value='',
label=ugettext_lazy('WLAN password'),
help_text=ugettext_lazy('Used for WLAN QRCode in PDF of access data.'),
weight=560,
group=ugettext_lazy('Users'),
subgroup=ugettext_lazy('PDF'))
yield ConfigVariable(
name='users_pdf_wlan_encryption',
default_value='',
input_type='choice',
label=ugettext_lazy('WLAN encryption'),
help_text=ugettext_lazy('Used for WLAN QRCode in PDF of access data.'),
choices=(
{'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'))

View File

@ -1,95 +1,9 @@
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
from django.db.models import Q from django.db.models import Q
from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy
from ..core.config import ConfigVariable
from .models import Group, User from .models import Group, User
def setup_users_config(sender, **kwargs):
"""
Receiver function to setup all users config variables. They are grouped
in 'Sorting' and 'PDF'. This function is connected to the signal
openslides.core.signals.config_signal during app loading.
"""
# Sorting
yield ConfigVariable(
name='users_sort_users_by_first_name',
default_value=False,
input_type='boolean',
label=ugettext_lazy('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
yield ConfigVariable(
name='users_pdf_welcometitle',
default_value=_('Welcome to OpenSlides!'),
label=ugettext_lazy('Title for access data and welcome PDF'),
weight=520,
group=ugettext_lazy('Users'),
subgroup=ugettext_lazy('PDF'),
translatable=True)
yield ConfigVariable(
name='users_pdf_welcometext',
default_value=_('[Place for your welcome and help text.]'),
label=ugettext_lazy('Help text for access data and welcome PDF'),
weight=530,
group=ugettext_lazy('Users'),
subgroup=ugettext_lazy('PDF'),
translatable=True)
# TODO: Use Django's URLValidator here.
yield ConfigVariable(
name='users_pdf_url',
default_value='http://example.com:8000',
label=ugettext_lazy('System URL'),
help_text=ugettext_lazy('Used for QRCode in PDF of access data.'),
weight=540,
group=ugettext_lazy('Users'),
subgroup=ugettext_lazy('PDF'))
yield ConfigVariable(
name='users_pdf_wlan_ssid',
default_value='',
label=ugettext_lazy('WLAN name (SSID)'),
help_text=ugettext_lazy('Used for WLAN QRCode in PDF of access data.'),
weight=550,
group=ugettext_lazy('Users'),
subgroup=ugettext_lazy('PDF'))
yield ConfigVariable(
name='users_pdf_wlan_password',
default_value='',
label=ugettext_lazy('WLAN password'),
help_text=ugettext_lazy('Used for WLAN QRCode in PDF of access data.'),
weight=560,
group=ugettext_lazy('Users'),
subgroup=ugettext_lazy('PDF'))
yield ConfigVariable(
name='users_pdf_wlan_encryption',
default_value='',
input_type='choice',
label=ugettext_lazy('WLAN encryption'),
help_text=ugettext_lazy('Used for WLAN QRCode in PDF of access data.'),
choices=(
{'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): def create_builtin_groups_and_admin(**kwargs):
""" """
Creates the builtin groups: Anonymous, Registered, Delegates and Staff. Creates the builtin groups: Anonymous, Registered, Delegates and Staff.

View File

@ -1,8 +1,6 @@
from django.test import TestCase as _TestCase from django.test import TestCase as _TestCase
from django.test.runner import DiscoverRunner from django.test.runner import DiscoverRunner
from openslides.core.config import config
class OpenSlidesDiscoverRunner(DiscoverRunner): class OpenSlidesDiscoverRunner(DiscoverRunner):
def run_tests(self, test_labels, extra_tests=None, **kwargs): def run_tests(self, test_labels, extra_tests=None, **kwargs):
@ -28,15 +26,8 @@ class OpenSlidesDiscoverRunner(DiscoverRunner):
class TestCase(_TestCase): class TestCase(_TestCase):
""" """
Overwrites Django's TestCase class to refreshs the config cache. Does nothing at the moment.
"""
def _post_teardown(self, *args, **kwargs): Could be used in the future. Use this this for the integration test suit.
return_value = super(TestCase, self)._post_teardown(*args, **kwargs) """
# Resets the config object by deleting the cache
try:
del config._cache
except AttributeError:
# The cache has only to be deleted if it exists.
pass pass
return return_value

View File

@ -1,17 +1,18 @@
import json import json
from unittest.mock import patch
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.dispatch import receiver
from rest_framework import status from rest_framework import status
from rest_framework.test import APIClient from rest_framework.test import APIClient
from openslides import __version__ as version from openslides import __version__ as version
from openslides.core.config import ConfigVariable, config from openslides.core.config import ConfigHandler, ConfigVariable
from openslides.core.models import CustomSlide, Projector from openslides.core.models import CustomSlide, Projector
from openslides.core.signals import config_signal
from openslides.utils.rest_api import ValidationError from openslides.utils.rest_api import ValidationError
from openslides.utils.test import TestCase from openslides.utils.test import TestCase
config = ConfigHandler()
class ProjectorAPI(TestCase): class ProjectorAPI(TestCase):
""" """
@ -74,10 +75,19 @@ class VersionView(TestCase):
'version': 'unknown'}]}) 'version': 'unknown'}]})
@patch('openslides.core.config.config', config)
@patch('openslides.core.views.config', config)
class ConfigViewSet(TestCase): class ConfigViewSet(TestCase):
""" """
Tests requests to deal with config variables. Tests requests to deal with config variables.
""" """
def setUp(self):
config.update_config_varialbes(set_simple_config_view_integration_config_test())
def tearDown(self):
# Reset the config variables
config.config_variables = {}
def test_retrieve(self): def test_retrieve(self):
self.client.login(username='admin', password='admin') self.client.login(username='admin', password='admin')
config['test_var_aeW3Quahkah1phahCheo'] = 'test_value_Oovoojieme7eephaed2A' config['test_var_aeW3Quahkah1phahCheo'] = 'test_value_Oovoojieme7eephaed2A'
@ -178,8 +188,7 @@ def validator_for_testing(value):
raise ValidationError({'detail': 'Invalid input.'}) raise ValidationError({'detail': 'Invalid input.'})
@receiver(config_signal, dispatch_uid='set_simple_config_view_integration_config_test') def set_simple_config_view_integration_config_test():
def set_simple_config_view_integration_config_test(sender, **kwargs):
""" """
Sets a simple config view with some config variables but without Sets a simple config view with some config variables but without
grouping. grouping.

View File

@ -150,7 +150,7 @@ class RetrieveMotion(TestCase):
self.motion.create_poll() self.motion.create_poll()
def test_number_of_queries(self): def test_number_of_queries(self):
with self.assertNumQueries(17): with self.assertNumQueries(16):
self.client.get(reverse('motion-detail', args=[self.motion.pk])) self.client.get(reverse('motion-detail', args=[self.motion.pk]))

View File

@ -1,12 +1,28 @@
from django.dispatch import receiver from unittest.mock import patch
from openslides.core.config import ConfigVariable, config from openslides.core.config import ConfigHandler, ConfigVariable
from openslides.core.exceptions import ConfigError, ConfigNotFound from openslides.core.exceptions import ConfigError, ConfigNotFound
from openslides.core.signals import config_signal
from openslides.utils.test import TestCase from openslides.utils.test import TestCase
class TestConfigException(Exception):
pass
config = ConfigHandler()
@patch('openslides.core.config.config', config)
class HandleConfigTest(TestCase): class HandleConfigTest(TestCase):
def setUp(self):
config.update_config_varialbes(set_grouped_config_view())
config.update_config_varialbes(set_simple_config_view())
config.update_config_varialbes(set_simple_config_view_multiple_vars())
config.update_config_varialbes(set_simple_config_collection_disabled_view())
config.update_config_varialbes(set_simple_config_collection_with_callback())
def tearDown(self):
# Reset the config variables
config.config_variables = {}
def get_config_var(self, key): def get_config_var(self, key):
return config[key] return config[key]
@ -26,24 +42,10 @@ class HandleConfigTest(TestCase):
self.get_config_var('unknown_config_var') self.get_config_var('unknown_config_var')
def test_get_multiple_config_var_error(self): def test_get_multiple_config_var_error(self):
config_signal.connect(
set_simple_config_view_multiple_vars,
dispatch_uid='set_simple_config_view_multiple_vars_for_testing')
with self.assertRaisesMessage( with self.assertRaisesMessage(
ConfigError, ConfigError,
'Too many values for config variable multiple_config_var found.'): 'Too many values for config variable multiple_config_var found.'):
config.setup_cache() config.update_config_varialbes(set_simple_config_view_multiple_vars())
config_signal.disconnect(
set_simple_config_view_multiple_vars,
dispatch_uid='set_simple_config_view_multiple_vars_for_testing')
def test_database_queries(self):
"""
Test that no database queries are send, after the cache was created.
"""
config.setup_cache()
self.assertNumQueries(0, self.get_config_var, key='string_var')
def test_setup_config_var(self): def test_setup_config_var(self):
self.assertRaises(TypeError, ConfigVariable) self.assertRaises(TypeError, ConfigVariable)
@ -73,9 +75,8 @@ class HandleConfigTest(TestCase):
Tests that the special callback is called and raises a special Tests that the special callback is called and raises a special
message. message.
""" """
# TODO: use right exception
with self.assertRaisesMessage( with self.assertRaisesMessage(
Exception, TestConfigException,
'Change callback dhcnfg34dlg06kdg successfully called.'): 'Change callback dhcnfg34dlg06kdg successfully called.'):
self.set_config_var( self.set_config_var(
key='var_with_callback_ghvnfjd5768gdfkwg0hm2', key='var_with_callback_ghvnfjd5768gdfkwg0hm2',
@ -86,8 +87,7 @@ class HandleConfigTest(TestCase):
'new_string_kbmbnfhdgibkdjshg452bc') 'new_string_kbmbnfhdgibkdjshg452bc')
@receiver(config_signal, dispatch_uid='set_grouped_config_view_for_testing') def set_grouped_config_view():
def set_grouped_config_view(sender, **kwargs):
""" """
Sets a grouped config collection. 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, with a string as default value, one with a boolean as default value,
@ -128,8 +128,7 @@ def set_grouped_config_view(sender, **kwargs):
subgroup='Group 2 Toongai7ahyahy7B') subgroup='Group 2 Toongai7ahyahy7B')
@receiver(config_signal, dispatch_uid='set_simple_config_view_for_testing') def set_simple_config_view():
def set_simple_config_view(sender, **kwargs):
""" """
Sets a simple config view with some config variables but without Sets a simple config view with some config variables but without
grouping. grouping.
@ -139,8 +138,7 @@ def set_simple_config_view(sender, **kwargs):
yield ConfigVariable(name='none_config_var', default_value=None) yield ConfigVariable(name='none_config_var', default_value=None)
# Do not connect to the signal now but later inside the test. def set_simple_config_view_multiple_vars():
def set_simple_config_view_multiple_vars(sender, **kwargs):
""" """
Sets a bad config view with some multiple config vars. Sets a bad config view with some multiple config vars.
""" """
@ -148,15 +146,13 @@ def set_simple_config_view_multiple_vars(sender, **kwargs):
yield ConfigVariable(name='multiple_config_var', default_value='foobar2') 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():
def set_simple_config_collection_disabled_view(sender, **kwargs):
yield 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():
def set_simple_config_collection_with_callback(sender, **kwargs):
def callback(): def callback():
raise Exception('Change callback dhcnfg34dlg06kdg successfully called.') raise TestConfigException('Change callback dhcnfg34dlg06kdg successfully called.')
yield ConfigVariable( yield ConfigVariable(
name='var_with_callback_ghvnfjd5768gdfkwg0hm2', name='var_with_callback_ghvnfjd5768gdfkwg0hm2',
default_value='', default_value='',

View File

@ -1,7 +1,7 @@
from unittest import TestCase from unittest import TestCase
from unittest.mock import patch from unittest.mock import patch
from openslides.core.config import ConfigHandler, ConfigVariable, config from openslides.core.config import ConfigVariable, config
from openslides.core.exceptions import ConfigNotFound from openslides.core.exceptions import ConfigNotFound
@ -26,12 +26,7 @@ class TestConfigVariable(TestCase):
class TestConfigHandler(TestCase): class TestConfigHandler(TestCase):
def test_get_from_cache(self):
ConfigHandler._cache = {'key_eeshah4Sho6zailee4ko': 'value_chies7aCohZoo9umieph'}
self.assertEqual(config['key_eeshah4Sho6zailee4ko'], 'value_chies7aCohZoo9umieph')
def test_get_not_found(self): def test_get_not_found(self):
ConfigHandler._cache = {}
self.assertRaises( self.assertRaises(
ConfigNotFound, ConfigNotFound,
config.__getitem__, config.__getitem__,