Merge pull request #2175 from ostcar/config
Redesign of the config variables.
This commit is contained in:
commit
f2570551a1
@ -8,6 +8,9 @@ Version 2.0.1 (unreleased)
|
||||
==========================
|
||||
[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)
|
||||
========================
|
||||
|
@ -15,16 +15,18 @@ class AgendaAppConfig(AppConfig):
|
||||
|
||||
# Import all required stuff.
|
||||
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 .config_variables import get_config_variables
|
||||
from .signals import (
|
||||
setup_agenda_config,
|
||||
listen_to_related_object_post_delete,
|
||||
listen_to_related_object_post_save)
|
||||
from .views import ItemViewSet
|
||||
|
||||
# Define config varialbes
|
||||
config.update_config_varialbes(get_config_variables())
|
||||
|
||||
# Connect signals.
|
||||
config_signal.connect(setup_agenda_config, dispatch_uid='setup_agenda_config')
|
||||
post_save.connect(
|
||||
listen_to_related_object_post_save,
|
||||
dispatch_uid='listen_to_related_object_post_save')
|
||||
|
88
openslides/agenda/config_variables.py
Normal file
88
openslides/agenda/config_variables.py
Normal 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'))
|
@ -1,97 +1,10 @@
|
||||
from datetime import datetime
|
||||
|
||||
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 .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):
|
||||
"""
|
||||
Receiver function to create agenda items. It is connected to the signal
|
||||
|
@ -14,13 +14,13 @@ class AssignmentsAppConfig(AppConfig):
|
||||
from . import projector # noqa
|
||||
|
||||
# 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 .signals import setup_assignment_config
|
||||
from .config_variables import get_config_variables
|
||||
from .views import AssignmentViewSet, AssignmentPollViewSet
|
||||
|
||||
# Connect signals.
|
||||
config_signal.connect(setup_assignment_config, dispatch_uid='setup_assignment_config')
|
||||
# Define config variables
|
||||
config.update_config_varialbes(get_config_variables())
|
||||
|
||||
# Register viewsets.
|
||||
router.register(self.get_model('Assignment').get_collection_string(), AssignmentViewSet)
|
||||
|
@ -6,15 +6,14 @@ from openslides.core.config import ConfigVariable
|
||||
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
|
||||
grouped in 'Ballot and ballot papers' and 'PDF'. This function is
|
||||
connected to the signal openslides.core.signals.config_signal during
|
||||
app loading.
|
||||
Generator which yields all config variables of this app.
|
||||
|
||||
They are grouped in 'Ballot and ballot papers' and 'PDF'. The generator has
|
||||
to be evaluated during app loading (see apps.py).
|
||||
"""
|
||||
# Ballot and ballot papers
|
||||
|
||||
yield ConfigVariable(
|
||||
name='assignments_poll_vote_values',
|
||||
default_value='auto',
|
@ -15,11 +15,13 @@ class CoreAppConfig(AppConfig):
|
||||
|
||||
# Import all required stuff.
|
||||
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.rest_api import router
|
||||
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 (
|
||||
ChatMessageViewSet,
|
||||
ConfigViewSet,
|
||||
@ -28,10 +30,10 @@ class CoreAppConfig(AppConfig):
|
||||
TagViewSet,
|
||||
)
|
||||
|
||||
# Define config variables
|
||||
config.update_config_varialbes(get_config_variables())
|
||||
|
||||
# Connect signals.
|
||||
config_signal.connect(
|
||||
setup_general_config,
|
||||
dispatch_uid='setup_general_config')
|
||||
post_permission_creation.connect(
|
||||
delete_django_app_permissions,
|
||||
dispatch_uid='delete_django_app_permissions')
|
||||
|
@ -19,33 +19,35 @@ class ConfigHandler:
|
||||
object. To get a config variable use x = config[...], to set it use
|
||||
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):
|
||||
"""
|
||||
Returns the value of the config variable. Builds the cache if it
|
||||
does not exist.
|
||||
Returns the value of the config variable. Returns the default value, if
|
||||
not value exists in the database.
|
||||
"""
|
||||
try:
|
||||
return self._cache[key]
|
||||
default_value = self.config_variables[key].default_value
|
||||
except KeyError:
|
||||
raise ConfigNotFound(_('The config variable %s was not found.') % key)
|
||||
except AttributeError:
|
||||
self.setup_cache()
|
||||
return self[key]
|
||||
raise ConfigNotFound(_('The config variable {} was not found.').format(key))
|
||||
|
||||
def setup_cache(self):
|
||||
"""
|
||||
Creates a cache of all config variables with their current value.
|
||||
"""
|
||||
self._cache = {}
|
||||
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
|
||||
try:
|
||||
db_value = ConfigStore.objects.get(key=key)
|
||||
except ConfigStore.DoesNotExist:
|
||||
return default_value
|
||||
return db_value.value
|
||||
|
||||
def __contains__(self, key):
|
||||
"""
|
||||
Returns True, if the config varialbe exists.
|
||||
"""
|
||||
try:
|
||||
config[key]
|
||||
except ConfigNotFound:
|
||||
self.config_variables[key]
|
||||
except KeyError:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
@ -56,9 +58,9 @@ class ConfigHandler:
|
||||
"""
|
||||
# Check if the variable is defined.
|
||||
try:
|
||||
config_variable = config.get_config_variables()[key]
|
||||
config_variable = self.config_variables[key]
|
||||
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.
|
||||
expected_type = INPUT_TYPE_MAPPING[config_variable.input_type]
|
||||
@ -69,7 +71,14 @@ class ConfigHandler:
|
||||
except ValueError:
|
||||
raise ConfigError(_('Wrong datatype. Expected %(expected_type)s, got %(got_type)s.') % {
|
||||
'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.'))
|
||||
for validator in config_variable.validators:
|
||||
try:
|
||||
@ -78,50 +87,45 @@ class ConfigHandler:
|
||||
raise ConfigError(e.messages[0])
|
||||
|
||||
# Save the new value to the database.
|
||||
config_store, created = ConfigStore.objects.get_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
|
||||
ConfigStore.objects.update_or_create(key=key, defaults={'value': value})
|
||||
|
||||
# Call on_change callback.
|
||||
if 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):
|
||||
"""
|
||||
Returns key-value pairs of all config variables.
|
||||
Iterates over key-value pairs of all config variables.
|
||||
"""
|
||||
if not hasattr(self, '_cache'):
|
||||
self.setup_cache()
|
||||
return self._cache.items()
|
||||
# Create a dict with the default values of each ConfigVariable
|
||||
config_items = dict((key, variable.default_value) for key, variable in self.config_variables.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.
|
||||
"""
|
||||
# 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
|
||||
# Update the dict with all values, which are in the db
|
||||
for db_value in ConfigStore.objects.all():
|
||||
config_items[db_value.key] = db_value.value
|
||||
return config_items.items()
|
||||
|
||||
def get_all_translatable(self):
|
||||
"""
|
||||
Generator to get all config variables as strings when their values are
|
||||
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:
|
||||
yield config_variable.name
|
||||
|
||||
|
160
openslides/core/config_variables.py
Normal file
160
openslides/core/config_variables.py
Normal 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'))
|
@ -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()
|
@ -1,12 +1,7 @@
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.validators import MaxLengthValidator
|
||||
from django.db.models import Q
|
||||
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
|
||||
# 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'))
|
||||
for permission in Permission.objects.filter(content_type__in=contenttypes):
|
||||
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."""
|
||||
|
@ -415,12 +415,9 @@ 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:
|
||||
for config_variable in sorted(config.config_variables.values(), key=attrgetter('weight')):
|
||||
if config_variable.is_hidden():
|
||||
# Skip hidden config variables. Do not even check groups and subgroups.
|
||||
continue
|
||||
|
@ -60,7 +60,6 @@ MIDDLEWARE_CLASSES = (
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'openslides.users.auth.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'openslides.core.middleware.ConfigCacheMiddleware',
|
||||
)
|
||||
|
||||
ROOT_URLCONF = 'openslides.urls'
|
||||
|
@ -15,13 +15,16 @@ class MotionsAppConfig(AppConfig):
|
||||
from . import projector # noqa
|
||||
|
||||
# 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 .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
|
||||
|
||||
# Define config varialbes
|
||||
config.update_config_varialbes(get_config_variables())
|
||||
|
||||
# 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')
|
||||
|
||||
# Register viewsets.
|
||||
|
183
openslides/motions/config_variables.py
Normal file
183
openslides/motions/config_variables.py
Normal 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'))
|
@ -1,179 +1,8 @@
|
||||
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.core.config import ConfigVariable
|
||||
from openslides.poll.models import PERCENT_BASE_CHOICES
|
||||
from django.utils.translation import ugettext_noop
|
||||
|
||||
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):
|
||||
"""
|
||||
Receiver function to create a simple and a complex workflow. It is
|
||||
|
@ -14,15 +14,17 @@ class UsersAppConfig(AppConfig):
|
||||
from . import projector # noqa
|
||||
|
||||
# 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 .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
|
||||
|
||||
# Define config variables
|
||||
config.update_config_varialbes(get_config_variables())
|
||||
|
||||
# Connect signals.
|
||||
config_signal.connect(
|
||||
setup_users_config,
|
||||
dispatch_uid='setup_users_config')
|
||||
post_permission_creation.connect(
|
||||
create_builtin_groups_and_admin,
|
||||
dispatch_uid='create_builtin_groups_and_admin')
|
||||
|
86
openslides/users/config_variables.py
Normal file
86
openslides/users/config_variables.py
Normal 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'))
|
@ -1,95 +1,9 @@
|
||||
from django.contrib.auth.models import Permission
|
||||
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
|
||||
|
||||
|
||||
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):
|
||||
"""
|
||||
Creates the builtin groups: Anonymous, Registered, Delegates and Staff.
|
||||
|
@ -1,8 +1,6 @@
|
||||
from django.test import TestCase as _TestCase
|
||||
from django.test.runner import DiscoverRunner
|
||||
|
||||
from openslides.core.config import config
|
||||
|
||||
|
||||
class OpenSlidesDiscoverRunner(DiscoverRunner):
|
||||
def run_tests(self, test_labels, extra_tests=None, **kwargs):
|
||||
@ -28,15 +26,8 @@ class OpenSlidesDiscoverRunner(DiscoverRunner):
|
||||
|
||||
class TestCase(_TestCase):
|
||||
"""
|
||||
Overwrites Django's TestCase class to refreshs the config cache.
|
||||
"""
|
||||
Does nothing at the moment.
|
||||
|
||||
def _post_teardown(self, *args, **kwargs):
|
||||
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.
|
||||
Could be used in the future. Use this this for the integration test suit.
|
||||
"""
|
||||
pass
|
||||
return return_value
|
||||
|
@ -1,17 +1,18 @@
|
||||
import json
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.dispatch import receiver
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
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.signals import config_signal
|
||||
from openslides.utils.rest_api import ValidationError
|
||||
from openslides.utils.test import TestCase
|
||||
|
||||
config = ConfigHandler()
|
||||
|
||||
|
||||
class ProjectorAPI(TestCase):
|
||||
"""
|
||||
@ -74,10 +75,19 @@ class VersionView(TestCase):
|
||||
'version': 'unknown'}]})
|
||||
|
||||
|
||||
@patch('openslides.core.config.config', config)
|
||||
@patch('openslides.core.views.config', config)
|
||||
class ConfigViewSet(TestCase):
|
||||
"""
|
||||
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):
|
||||
self.client.login(username='admin', password='admin')
|
||||
config['test_var_aeW3Quahkah1phahCheo'] = 'test_value_Oovoojieme7eephaed2A'
|
||||
@ -178,8 +188,7 @@ def validator_for_testing(value):
|
||||
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(sender, **kwargs):
|
||||
def set_simple_config_view_integration_config_test():
|
||||
"""
|
||||
Sets a simple config view with some config variables but without
|
||||
grouping.
|
||||
|
@ -150,7 +150,7 @@ class RetrieveMotion(TestCase):
|
||||
self.motion.create_poll()
|
||||
|
||||
def test_number_of_queries(self):
|
||||
with self.assertNumQueries(17):
|
||||
with self.assertNumQueries(16):
|
||||
self.client.get(reverse('motion-detail', args=[self.motion.pk]))
|
||||
|
||||
|
||||
|
@ -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.signals import config_signal
|
||||
from openslides.utils.test import TestCase
|
||||
|
||||
|
||||
class TestConfigException(Exception):
|
||||
pass
|
||||
|
||||
config = ConfigHandler()
|
||||
|
||||
|
||||
@patch('openslides.core.config.config', config)
|
||||
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):
|
||||
return config[key]
|
||||
@ -26,24 +42,10 @@ class HandleConfigTest(TestCase):
|
||||
self.get_config_var('unknown_config_var')
|
||||
|
||||
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(
|
||||
ConfigError,
|
||||
'Too many values for config variable multiple_config_var found.'):
|
||||
config.setup_cache()
|
||||
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')
|
||||
config.update_config_varialbes(set_simple_config_view_multiple_vars())
|
||||
|
||||
def test_setup_config_var(self):
|
||||
self.assertRaises(TypeError, ConfigVariable)
|
||||
@ -73,9 +75,8 @@ class HandleConfigTest(TestCase):
|
||||
Tests that the special callback is called and raises a special
|
||||
message.
|
||||
"""
|
||||
# TODO: use right exception
|
||||
with self.assertRaisesMessage(
|
||||
Exception,
|
||||
TestConfigException,
|
||||
'Change callback dhcnfg34dlg06kdg successfully called.'):
|
||||
self.set_config_var(
|
||||
key='var_with_callback_ghvnfjd5768gdfkwg0hm2',
|
||||
@ -86,8 +87,7 @@ class HandleConfigTest(TestCase):
|
||||
'new_string_kbmbnfhdgibkdjshg452bc')
|
||||
|
||||
|
||||
@receiver(config_signal, dispatch_uid='set_grouped_config_view_for_testing')
|
||||
def set_grouped_config_view(sender, **kwargs):
|
||||
def set_grouped_config_view():
|
||||
"""
|
||||
Sets a grouped config collection. There are some variables, one variable
|
||||
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')
|
||||
|
||||
|
||||
@receiver(config_signal, dispatch_uid='set_simple_config_view_for_testing')
|
||||
def set_simple_config_view(sender, **kwargs):
|
||||
def set_simple_config_view():
|
||||
"""
|
||||
Sets a simple config view with some config variables but without
|
||||
grouping.
|
||||
@ -139,8 +138,7 @@ def set_simple_config_view(sender, **kwargs):
|
||||
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(sender, **kwargs):
|
||||
def set_simple_config_view_multiple_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')
|
||||
|
||||
|
||||
@receiver(config_signal, dispatch_uid='set_simple_config_collection_disabled_view_for_testing')
|
||||
def set_simple_config_collection_disabled_view(sender, **kwargs):
|
||||
def set_simple_config_collection_disabled_view():
|
||||
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 set_simple_config_collection_with_callback():
|
||||
def callback():
|
||||
raise Exception('Change callback dhcnfg34dlg06kdg successfully called.')
|
||||
raise TestConfigException('Change callback dhcnfg34dlg06kdg successfully called.')
|
||||
yield ConfigVariable(
|
||||
name='var_with_callback_ghvnfjd5768gdfkwg0hm2',
|
||||
default_value='',
|
||||
|
@ -1,7 +1,7 @@
|
||||
from unittest import TestCase
|
||||
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
|
||||
|
||||
|
||||
@ -26,12 +26,7 @@ class TestConfigVariable(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):
|
||||
ConfigHandler._cache = {}
|
||||
self.assertRaises(
|
||||
ConfigNotFound,
|
||||
config.__getitem__,
|
||||
|
Loading…
Reference in New Issue
Block a user