diff --git a/CHANGELOG b/CHANGELOG index d965a89cd..cd5ca7186 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -36,6 +36,7 @@ Other: - Refactored projector API using metaclasses now. - Renamed SignalConnectMetaClass classmethod get_all_objects to get_all (private API). +- Refactored config API. - Used AngularJS with additional libraries for single page frontend. - Removed use of 'django.views.i18n.javascript_catalog'. Used angular-gettext now. diff --git a/openslides/config/api.py b/openslides/config/api.py index 1b7acbb8b..1498ec0ef 100644 --- a/openslides/config/api.py +++ b/openslides/config/api.py @@ -19,25 +19,21 @@ class ConfigHandler(object): return self[key] def __setitem__(self, key, value): - # Save the new value to the database + # Check if the variable is defined. + if key not in self: + raise ConfigNotFound('The config variable %s was not found.' % key) + + # Save the new value to the database. updated_rows = ConfigStore.objects.filter(key=key).update(value=value) if not updated_rows: ConfigStore.objects.create(key=key, value=value) - # Update cache - try: - self._cache[key] = value - except AttributeError: - # This happens, when a config-var is set, before __getitem__ was - # called. In this case nothing should happen. - pass + # Update cache. + self._cache[key] = value - # Call on_change callback - for receiver, config_collection in config_signal.send(sender='set_value'): - for config_variable in config_collection.variables: - if config_variable.name == key and config_variable.on_change: - config_variable.on_change() - break + # Call on_change callback. + if self.get_config_variables()[key].on_change: + self.get_config_variables()[key].on_change() def items(self): """ @@ -47,15 +43,27 @@ class ConfigHandler(object): self.setup_cache() return self._cache.items() + def get_config_variables(self): + """ + Returns a dictionary with all ConfigVariable instances of all + collections. The key is the name of the config variables. + """ + result = {} + for receiver, config_collection in config_signal.send(sender='get_config_variables'): + for config_variable in config_collection.variables: + if config_variable.name in result: + raise ConfigError('Too many values for config variable %s found.' % config_variable.name) + result[config_variable.name] = config_variable + return result + def get_default(self, key): """ Returns the default value for 'key'. """ - for receiver, config_collection in config_signal.send(sender='get_default'): - for config_variable in config_collection.variables: - if config_variable.name == key: - return config_variable.default_value - raise ConfigNotFound('The config variable %s was not found.' % key) + try: + return self.get_config_variables()[key].default_value + except KeyError: + raise ConfigNotFound('The config variable %s was not found.' % key) def setup_cache(self): """ @@ -63,11 +71,8 @@ class ConfigHandler(object): save the default to the cache. """ self._cache = {} - for receiver, config_collection in config_signal.send(sender='setup_cache'): - for config_variable in config_collection.variables: - if config_variable.name in self._cache: - raise ConfigError('Too many values for config variable %s found.' % config_variable.name) - self._cache[config_variable.name] = config_variable.default_value + 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 @@ -84,10 +89,9 @@ class ConfigHandler(object): Generator to get all config variables as strings when their values are intended to be translated. """ - for receiver, config_collection in config_signal.send(sender='get_all_translatable'): - for config_variable in config_collection.variables: - if config_variable.translatable: - yield config_variable.name + for config_variable in self.get_config_variables().values(): + if config_variable.translatable: + yield config_variable.name config = ConfigHandler() """ diff --git a/openslides/config/views.py b/openslides/config/views.py index 5fd7c8859..ba04465c7 100644 --- a/openslides/config/views.py +++ b/openslides/config/views.py @@ -1,10 +1,11 @@ from django import forms from django.contrib import messages from django.core.urlresolvers import reverse +from django.core.exceptions import ValidationError as DjangoValidationError from django.http import Http404 from django.utils.translation import ugettext as _ -from openslides.utils.rest_api import Response, ViewSet +from openslides.utils.rest_api import Response, ValidationError, ViewSet from openslides.utils.views import FormView from .api import config @@ -119,7 +120,7 @@ class ConfigViewSet(ViewSet): def retrieve(self, request, *args, **kwargs): """ - Retrieves one config variable. + Retrieves one config variable. Everybody can see it. """ # TODO: Check if we need permission check here. key = kwargs['pk'] @@ -129,12 +130,32 @@ class ConfigViewSet(ViewSet): raise Http404 return Response(data) - def update(self, request, pk=None): + def update(self, request, *args, **kwargs): """ - TODO + Updates one config variable. Only managers can do this. + + Example: {"value": 42} """ + # Check permission. if not request.user.has_perm('config.can_manage'): self.permission_denied(request) - else: - # TODO: Implement update method - self.permission_denied(request) + + # Check if pk is a valid config variable key. + key = kwargs['pk'] + if key not in config: + raise Http404 + + # Validate value. + form_field = config.get_config_variables()[key].form_field + value = request.data['value'] + if form_field: + try: + form_field.clean(value) + except DjangoValidationError as e: + raise ValidationError({'detail': e.messages[0]}) + + # Change value. + config[key] = value + + # Return response. + return Response({'key': key, 'value': value}) diff --git a/tests/integration/config/__init__.py b/tests/integration/config/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/integration/config/test_views.py b/tests/integration/config/test_views.py new file mode 100644 index 000000000..39cfc163d --- /dev/null +++ b/tests/integration/config/test_views.py @@ -0,0 +1,60 @@ +from django import forms +from django.dispatch import receiver +from django.core.urlresolvers import reverse +from rest_framework import status +from rest_framework.test import APIClient + +from openslides.config.api import config, ConfigCollection, ConfigVariable +from openslides.config.signals import config_signal +from openslides.utils.test import TestCase + + +class ConfigViewSet(TestCase): + """ + Tests requests to deal with config variables. + """ + def test_retrieve(self): + self.client.login(username='admin', password='admin') + config['test_var_aeW3Quahkah1phahCheo'] = 'test_value_Oovoojieme7eephaed2A' + response = self.client.get(reverse('config-detail', args=['test_var_aeW3Quahkah1phahCheo'])) + self.assertEqual( + response.data, + {'key': 'test_var_aeW3Quahkah1phahCheo', + 'value': 'test_value_Oovoojieme7eephaed2A'}) + + def test_update(self): + self.client = APIClient() + self.client.login(username='admin', password='admin') + response = self.client.put( + reverse('config-detail', args=['test_var_Xeiizi7ooH8Thuk5aida']), + {'value': 'test_value_Phohx3oopeichaiTheiw'}) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(config['test_var_Xeiizi7ooH8Thuk5aida'], 'test_value_Phohx3oopeichaiTheiw') + + def test_update_wrong_datatype(self): + self.client = APIClient() + self.client.login(username='admin', password='admin') + response = self.client.put( + reverse('config-detail', args=['test_var_ohhii4iavoh5Phoh5ahg']), + {'value': 'test_value_string'}) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response.data, {'detail': 'Enter a whole number.'}) + + +@receiver(config_signal, dispatch_uid='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 + grouping. + """ + return ConfigCollection( + title='Config vars for testing', + url='test_url_ieXao5Wae5Duoy6Wohtu', + variables=(ConfigVariable(name='test_var_aeW3Quahkah1phahCheo', + default_value=None), + ConfigVariable(name='test_var_Xeiizi7ooH8Thuk5aida', + default_value='', + form_field=forms.CharField()), + ConfigVariable(name='test_var_ohhii4iavoh5Phoh5ahg', + default_value=0, + form_field=forms.IntegerField()))) diff --git a/tests/old/config/test_config.py b/tests/old/config/test_config.py index 9c2c41edf..0b754af5a 100644 --- a/tests/old/config/test_config.py +++ b/tests/old/config/test_config.py @@ -67,9 +67,9 @@ class HandleConfigTest(TestCase): def test_set_value_before_getting_it(self): """ - Try to call __setitem__ before __getitem. + Try to call __setitem__ before __getitem__. """ - config['my_config_var'] = 'value' + config['additional_config_var'] = 'value' def test_on_change(self): """ diff --git a/tests/old/core/test_template_tags_filters.py b/tests/old/core/test_template_tags_filters.py index 71e695316..a45412a42 100644 --- a/tests/old/core/test_template_tags_filters.py +++ b/tests/old/core/test_template_tags_filters.py @@ -1,6 +1,8 @@ +from django.dispatch import receiver from django.template import Context, Template -from openslides.config.api import config +from openslides.config.api import ConfigCollection, ConfigVariable, config +from openslides.config.signals import config_signal from openslides.utils.test import TestCase @@ -27,3 +29,18 @@ class ConfigTagAndFilter(TestCase): template = Template(template_code) self.assertTrue('FdgfkR04jtg9f8bq' in template.render(Context({}))) self.assertFalse('bad_e0fvkfHFD' in template.render(Context({}))) + + +@receiver(config_signal, dispatch_uid='set_simple_config_view_template_tag_test') +def set_simple_config_view_template_tag_test(sender, **kwargs): + """ + Sets a simple config view with some config variables but without + grouping. + """ + return ConfigCollection( + title='Config vars for testing with template tag', + url='testsimplepagetemplatetag', + variables=(ConfigVariable(name='taiNg3reQuooGha4', default_value=None), + ConfigVariable(name='fkjTze56ncuejWqs', default_value=None), + ConfigVariable(name='jfhsnezfh452w6Fg', default_value=None), + ConfigVariable(name='sdmvldkfgj4534gk', default_value=None)))