Merge pull request #1579 from ostcar/config_into_core
Moved config app into the core app
This commit is contained in:
commit
db0a4224c2
@ -37,7 +37,7 @@ Other:
|
|||||||
- Refactored projector API using metaclasses now.
|
- Refactored projector API using metaclasses now.
|
||||||
- Renamed SignalConnectMetaClass classmethod get_all_objects to get_all
|
- Renamed SignalConnectMetaClass classmethod get_all_objects to get_all
|
||||||
(private API).
|
(private API).
|
||||||
- Refactored config API.
|
- Refactored config API and moved it into the core app.
|
||||||
- Removed old style personal info page, main menu entries and widget API.
|
- Removed old style personal info page, main menu entries and widget API.
|
||||||
- Used AngularJS with additional libraries for single page frontend.
|
- Used AngularJS with additional libraries for single page frontend.
|
||||||
- Removed use of 'django.views.i18n.javascript_catalog'. Used angular-gettext
|
- Removed use of 'django.views.i18n.javascript_catalog'. Used angular-gettext
|
||||||
|
@ -12,7 +12,7 @@ class AgendaAppConfig(AppConfig):
|
|||||||
|
|
||||||
# Import all required stuff.
|
# Import all required stuff.
|
||||||
from django.db.models.signals import pre_delete
|
from django.db.models.signals import pre_delete
|
||||||
from openslides.config.signals import config_signal
|
from openslides.core.signals import config_signal
|
||||||
from openslides.utils.rest_api import router
|
from openslides.utils.rest_api import router
|
||||||
from .signals import setup_agenda_config, listen_to_related_object_delete_signal
|
from .signals import setup_agenda_config, listen_to_related_object_delete_signal
|
||||||
from .views import ItemViewSet
|
from .views import ItemViewSet
|
||||||
|
@ -9,12 +9,12 @@ from django.db import models
|
|||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.utils.translation import ugettext_lazy, ugettext_noop
|
from django.utils.translation import ugettext_lazy, ugettext_noop
|
||||||
|
|
||||||
from openslides.config.api import config
|
from openslides.core.config import config
|
||||||
from openslides.core.models import Tag
|
from openslides.core.models import Tag
|
||||||
from openslides.projector.models import SlideMixin
|
from openslides.projector.models import SlideMixin
|
||||||
from openslides.users.models import User
|
from openslides.users.models import User
|
||||||
from openslides.utils.exceptions import OpenSlidesError
|
from openslides.utils.exceptions import OpenSlidesError
|
||||||
from openslides.utils.rest_api import RESTModelMixin
|
from openslides.utils.models import RESTModelMixin
|
||||||
from openslides.utils.utils import to_roman
|
from openslides.utils.utils import to_roman
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ from django.core.validators import MaxLengthValidator, MinValueValidator
|
|||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.utils.translation import ugettext_lazy
|
from django.utils.translation import ugettext_lazy
|
||||||
|
|
||||||
from openslides.config.api import ConfigVariable
|
from openslides.core.config import ConfigVariable
|
||||||
|
|
||||||
from .models import Item
|
from .models import Item
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ def setup_agenda_config(sender, **kwargs):
|
|||||||
"""
|
"""
|
||||||
Receiver function to setup all agenda config variables. They are not
|
Receiver function to setup all agenda config variables. They are not
|
||||||
grouped. This function connected to the signal
|
grouped. This function connected to the signal
|
||||||
openslides.config.signals.config_signal during app loading.
|
openslides.core.signals.config_signal during app loading.
|
||||||
"""
|
"""
|
||||||
# TODO: Use an input type with generic datetime support.
|
# TODO: Use an input type with generic datetime support.
|
||||||
yield ConfigVariable(
|
yield ConfigVariable(
|
||||||
|
@ -11,7 +11,7 @@ class AssignmentAppConfig(AppConfig):
|
|||||||
from . import projector # noqa
|
from . import projector # noqa
|
||||||
|
|
||||||
# Import all required stuff.
|
# Import all required stuff.
|
||||||
from openslides.config.signals import config_signal
|
from openslides.core.signals import config_signal
|
||||||
from openslides.utils.rest_api import router
|
from openslides.utils.rest_api import router
|
||||||
from .signals import setup_assignment_config
|
from .signals import setup_assignment_config
|
||||||
from .views import AssignmentViewSet, AssignmentPollViewSet
|
from .views import AssignmentViewSet, AssignmentPollViewSet
|
||||||
|
@ -5,7 +5,7 @@ from django.utils.translation import ugettext as _
|
|||||||
from django.utils.translation import ugettext_lazy, ugettext_noop
|
from django.utils.translation import ugettext_lazy, ugettext_noop
|
||||||
|
|
||||||
from openslides.agenda.models import Item, Speaker
|
from openslides.agenda.models import Item, Speaker
|
||||||
from openslides.config.api import config
|
from openslides.core.config import config
|
||||||
from openslides.core.models import Tag
|
from openslides.core.models import Tag
|
||||||
from openslides.poll.models import (
|
from openslides.poll.models import (
|
||||||
BaseOption,
|
BaseOption,
|
||||||
@ -17,7 +17,7 @@ from openslides.poll.models import (
|
|||||||
from openslides.projector.models import SlideMixin
|
from openslides.projector.models import SlideMixin
|
||||||
from openslides.users.models import User
|
from openslides.users.models import User
|
||||||
from openslides.utils.exceptions import OpenSlidesError
|
from openslides.utils.exceptions import OpenSlidesError
|
||||||
from openslides.utils.rest_api import RESTModelMixin
|
from openslides.utils.models import RESTModelMixin
|
||||||
|
|
||||||
|
|
||||||
class AssignmentRelatedUser(RESTModelMixin, models.Model):
|
class AssignmentRelatedUser(RESTModelMixin, models.Model):
|
||||||
|
@ -2,7 +2,7 @@ from django.core.validators import MinValueValidator
|
|||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.utils.translation import ugettext_lazy
|
from django.utils.translation import ugettext_lazy
|
||||||
|
|
||||||
from openslides.config.api import ConfigVariable
|
from openslides.core.config import ConfigVariable
|
||||||
from openslides.poll.models import PERCENT_BASE_CHOICES
|
from openslides.poll.models import PERCENT_BASE_CHOICES
|
||||||
|
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ def setup_assignment_config(sender, **kwargs):
|
|||||||
"""
|
"""
|
||||||
Receiver function to setup all assignment config variables. They are
|
Receiver function to setup all assignment config variables. They are
|
||||||
grouped in 'Ballot and ballot papers' and 'PDF'. This function is
|
grouped in 'Ballot and ballot papers' and 'PDF'. This function is
|
||||||
connected to the signal openslides.config.signals.config_signal during
|
connected to the signal openslides.core.signals.config_signal during
|
||||||
app loading.
|
app loading.
|
||||||
"""
|
"""
|
||||||
# Ballot and ballot papers
|
# Ballot and ballot papers
|
||||||
|
@ -15,7 +15,7 @@ from reportlab.platypus import (
|
|||||||
TableStyle,
|
TableStyle,
|
||||||
)
|
)
|
||||||
|
|
||||||
from openslides.config.api import config
|
from openslides.core.config import config
|
||||||
from openslides.users.models import Group, User # TODO: remove this
|
from openslides.users.models import Group, User # TODO: remove this
|
||||||
from openslides.utils.pdf import stylesheet
|
from openslides.utils.pdf import stylesheet
|
||||||
from openslides.utils.rest_api import (
|
from openslides.utils.rest_api import (
|
||||||
|
@ -1 +0,0 @@
|
|||||||
default_app_config = 'openslides.config.apps.ConfigAppConfig'
|
|
@ -1,14 +0,0 @@
|
|||||||
from django.apps import AppConfig
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigAppConfig(AppConfig):
|
|
||||||
name = 'openslides.config'
|
|
||||||
verbose_name = 'OpenSlides Config'
|
|
||||||
|
|
||||||
def ready(self):
|
|
||||||
# Import all required stuff.
|
|
||||||
from openslides.utils.rest_api import router
|
|
||||||
from .views import ConfigViewSet
|
|
||||||
|
|
||||||
# Register viewsets.
|
|
||||||
router.register('config/config', ConfigViewSet, 'config')
|
|
@ -1,9 +0,0 @@
|
|||||||
from openslides.utils.exceptions import OpenSlidesError
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigError(OpenSlidesError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigNotFound(ConfigError):
|
|
||||||
pass
|
|
@ -1,18 +0,0 @@
|
|||||||
from django.db import models
|
|
||||||
from django.utils.translation import ugettext_noop
|
|
||||||
from jsonfield import JSONField
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigStore(models.Model):
|
|
||||||
"""
|
|
||||||
A model class to store all config variables in the database.
|
|
||||||
"""
|
|
||||||
|
|
||||||
key = models.CharField(max_length=255, unique=True, db_index=True)
|
|
||||||
"""A string, the key of the config variable."""
|
|
||||||
|
|
||||||
value = JSONField()
|
|
||||||
"""The value of the config variable. """
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
permissions = (('can_manage', ugettext_noop('Can manage configuration')),)
|
|
@ -1,4 +0,0 @@
|
|||||||
from django.dispatch import Signal
|
|
||||||
|
|
||||||
config_signal = Signal(providing_args=[])
|
|
||||||
"""Signal to get all config tabs from all apps."""
|
|
@ -1,89 +0,0 @@
|
|||||||
from collections import OrderedDict
|
|
||||||
from operator import attrgetter
|
|
||||||
|
|
||||||
from django.http import Http404
|
|
||||||
|
|
||||||
from openslides.utils.rest_api import (
|
|
||||||
Response,
|
|
||||||
SimpleMetadata,
|
|
||||||
ValidationError,
|
|
||||||
ViewSet,
|
|
||||||
)
|
|
||||||
|
|
||||||
from .api import config
|
|
||||||
from .exceptions import ConfigError, ConfigNotFound
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigMetadata(SimpleMetadata):
|
|
||||||
"""
|
|
||||||
Custom metadata class to add config info to responses on OPTIONS requests.
|
|
||||||
"""
|
|
||||||
def determine_metadata(self, request, view):
|
|
||||||
# Sort config variables by weight.
|
|
||||||
config_variables = sorted(config.get_config_variables().values(), key=attrgetter('weight'))
|
|
||||||
|
|
||||||
# Build tree.
|
|
||||||
config_groups = []
|
|
||||||
for config_variable in config_variables:
|
|
||||||
if not config_groups or config_groups[-1]['name'] != config_variable.group:
|
|
||||||
config_groups.append(OrderedDict(
|
|
||||||
name=config_variable.group,
|
|
||||||
subgroups=[]))
|
|
||||||
if not config_groups[-1]['subgroups'] or config_groups[-1]['subgroups'][-1]['name'] != config_variable.subgroup:
|
|
||||||
config_groups[-1]['subgroups'].append(OrderedDict(
|
|
||||||
name=config_variable.subgroup,
|
|
||||||
items=[]))
|
|
||||||
config_groups[-1]['subgroups'][-1]['items'].append(config_variable.data)
|
|
||||||
|
|
||||||
# Add tree to metadata.
|
|
||||||
metadata = super().determine_metadata(request, view)
|
|
||||||
metadata['config_groups'] = config_groups
|
|
||||||
return metadata
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigViewSet(ViewSet):
|
|
||||||
"""
|
|
||||||
API endpoint to list, retrieve and update the config.
|
|
||||||
"""
|
|
||||||
metadata_class = ConfigMetadata
|
|
||||||
|
|
||||||
def list(self, request):
|
|
||||||
"""
|
|
||||||
Lists all config variables. Everybody can see them.
|
|
||||||
"""
|
|
||||||
return Response([{'key': key, 'value': value} for key, value in config.items()])
|
|
||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
Retrieves a config variable. Everybody can see it.
|
|
||||||
"""
|
|
||||||
key = kwargs['pk']
|
|
||||||
try:
|
|
||||||
value = config[key]
|
|
||||||
except ConfigNotFound:
|
|
||||||
raise Http404
|
|
||||||
return Response({'key': key, 'value': value})
|
|
||||||
|
|
||||||
def update(self, request, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
Updates a 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)
|
|
||||||
|
|
||||||
key = kwargs['pk']
|
|
||||||
value = request.data['value']
|
|
||||||
|
|
||||||
# Validate and change value.
|
|
||||||
try:
|
|
||||||
config[key] = value
|
|
||||||
except ConfigNotFound:
|
|
||||||
raise Http404
|
|
||||||
except ConfigError as e:
|
|
||||||
raise ValidationError({'detail': e})
|
|
||||||
|
|
||||||
# Return response.
|
|
||||||
return Response({'key': key, 'value': value})
|
|
@ -12,11 +12,16 @@ 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.config.signals import config_signal
|
from openslides.core.signals import config_signal
|
||||||
from openslides.utils.autoupdate import inform_changed_data_receiver
|
from openslides.utils.autoupdate import inform_changed_data_receiver
|
||||||
from openslides.utils.rest_api import router
|
from openslides.utils.rest_api import router
|
||||||
from .signals import setup_general_config
|
from .signals import setup_general_config
|
||||||
from .views import CustomSlideViewSet, ProjectorViewSet, TagViewSet
|
from .views import (
|
||||||
|
ConfigViewSet,
|
||||||
|
CustomSlideViewSet,
|
||||||
|
ProjectorViewSet,
|
||||||
|
TagViewSet,
|
||||||
|
)
|
||||||
|
|
||||||
# Connect signals.
|
# Connect signals.
|
||||||
config_signal.connect(setup_general_config, dispatch_uid='setup_general_config')
|
config_signal.connect(setup_general_config, dispatch_uid='setup_general_config')
|
||||||
@ -25,6 +30,7 @@ class CoreAppConfig(AppConfig):
|
|||||||
router.register('core/projector', ProjectorViewSet)
|
router.register('core/projector', ProjectorViewSet)
|
||||||
router.register('core/customslide', CustomSlideViewSet)
|
router.register('core/customslide', CustomSlideViewSet)
|
||||||
router.register('core/tag', TagViewSet)
|
router.register('core/tag', TagViewSet)
|
||||||
|
router.register('core/config', ConfigViewSet, 'config')
|
||||||
|
|
||||||
# Update data when any model of any installed app is saved or deleted.
|
# Update data when any model of any installed app is saved or deleted.
|
||||||
# TODO: Test if the m2m_changed signal is also needed.
|
# TODO: Test if the m2m_changed signal is also needed.
|
||||||
|
@ -3,7 +3,6 @@ from django.utils.translation import ugettext as _
|
|||||||
|
|
||||||
from .exceptions import ConfigError, ConfigNotFound
|
from .exceptions import ConfigError, ConfigNotFound
|
||||||
from .models import ConfigStore
|
from .models import ConfigStore
|
||||||
from .signals import config_signal
|
|
||||||
|
|
||||||
INPUT_TYPE_MAPPING = {
|
INPUT_TYPE_MAPPING = {
|
||||||
'string': str,
|
'string': str,
|
||||||
@ -97,6 +96,10 @@ class ConfigHandler:
|
|||||||
Returns a dictionary with all ConfigVariable instances of all
|
Returns a dictionary with all ConfigVariable instances of all
|
||||||
signal receivers. The key is the name of the config variable.
|
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 = {}
|
result = {}
|
||||||
for receiver, config_collection in config_signal.send(sender='get_config_variables'):
|
for receiver, config_collection in config_signal.send(sender='get_config_variables'):
|
||||||
for config_variable in config_collection:
|
for config_variable in config_collection:
|
@ -7,3 +7,11 @@ class ProjectorException(OpenSlidesError):
|
|||||||
|
|
||||||
class TagException(OpenSlidesError):
|
class TagException(OpenSlidesError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigError(OpenSlidesError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigNotFound(ConfigError):
|
||||||
|
pass
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from openslides.config.api import config
|
from .config import config
|
||||||
|
|
||||||
|
|
||||||
class ConfigCacheMiddleware(object):
|
class ConfigCacheMiddleware(object):
|
@ -2,7 +2,6 @@ import jsonfield.fields
|
|||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
import openslides.utils.models
|
import openslides.utils.models
|
||||||
import openslides.utils.rest_api
|
|
||||||
|
|
||||||
|
|
||||||
def add_default_projector(apps, schema_editor):
|
def add_default_projector(apps, schema_editor):
|
||||||
@ -32,20 +31,20 @@ class Migration(migrations.Migration):
|
|||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='CustomSlide',
|
name='CustomSlide',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(serialize=False, primary_key=True, auto_created=True, verbose_name='ID')),
|
('id', models.AutoField(auto_created=True, serialize=False, verbose_name='ID', primary_key=True)),
|
||||||
('title', models.CharField(verbose_name='Title', max_length=256)),
|
('title', models.CharField(max_length=256, verbose_name='Title')),
|
||||||
('text', models.TextField(verbose_name='Text', blank=True)),
|
('text', models.TextField(blank=True, verbose_name='Text')),
|
||||||
('weight', models.IntegerField(verbose_name='Weight', default=0)),
|
('weight', models.IntegerField(verbose_name='Weight', default=0)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ('weight', 'title'),
|
'ordering': ('weight', 'title'),
|
||||||
},
|
},
|
||||||
bases=(openslides.utils.rest_api.RESTModelMixin, models.Model),
|
bases=(openslides.utils.models.RESTModelMixin, models.Model),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Projector',
|
name='Projector',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(serialize=False, primary_key=True, auto_created=True, verbose_name='ID')),
|
('id', models.AutoField(auto_created=True, serialize=False, verbose_name='ID', primary_key=True)),
|
||||||
('config', jsonfield.fields.JSONField()),
|
('config', jsonfield.fields.JSONField()),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
@ -55,21 +54,35 @@ class Migration(migrations.Migration):
|
|||||||
('can_see_dashboard', 'Can see the dashboard'),
|
('can_see_dashboard', 'Can see the dashboard'),
|
||||||
('can_use_chat', 'Can use the chat')),
|
('can_use_chat', 'Can use the chat')),
|
||||||
},
|
},
|
||||||
bases=(openslides.utils.rest_api.RESTModelMixin, models.Model),
|
bases=(openslides.utils.models.RESTModelMixin, models.Model),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Tag',
|
name='Tag',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(serialize=False, primary_key=True, auto_created=True, verbose_name='ID')),
|
('id', models.AutoField(auto_created=True, serialize=False, verbose_name='ID', primary_key=True)),
|
||||||
('name', models.CharField(verbose_name='Tag', unique=True, max_length=255)),
|
('name', models.CharField(max_length=255, verbose_name='Tag', unique=True)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'permissions': (('can_manage_tags', 'Can manage tags'),),
|
|
||||||
'ordering': ('name',),
|
'ordering': ('name',),
|
||||||
|
'permissions': (('can_manage_tags', 'Can manage tags'),),
|
||||||
},
|
},
|
||||||
bases=(openslides.utils.rest_api.RESTModelMixin, models.Model),
|
bases=(openslides.utils.models.RESTModelMixin, models.Model),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ConfigStore',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, serialize=False, verbose_name='ID', primary_key=True)),
|
||||||
|
('key', models.CharField(max_length=255, db_index=True, unique=True)),
|
||||||
|
('value', jsonfield.fields.JSONField()),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'permissions': (('can_manage_config', 'Can manage configuration'),),
|
||||||
|
},
|
||||||
|
bases=(models.Model,),
|
||||||
),
|
),
|
||||||
migrations.RunPython(
|
migrations.RunPython(
|
||||||
add_default_projector,
|
code=add_default_projector,
|
||||||
|
reverse_code=None,
|
||||||
|
atomic=True,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
@ -3,8 +3,8 @@ from django.utils.translation import ugettext as _
|
|||||||
from django.utils.translation import ugettext_lazy, ugettext_noop
|
from django.utils.translation import ugettext_lazy, ugettext_noop
|
||||||
from jsonfield import JSONField
|
from jsonfield import JSONField
|
||||||
|
|
||||||
|
from openslides.utils.models import RESTModelMixin
|
||||||
from openslides.utils.projector import ProjectorElement
|
from openslides.utils.projector import ProjectorElement
|
||||||
from openslides.utils.rest_api import RESTModelMixin
|
|
||||||
|
|
||||||
from .exceptions import ProjectorException
|
from .exceptions import ProjectorException
|
||||||
|
|
||||||
@ -120,3 +120,18 @@ class Tag(RESTModelMixin, models.Model):
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigStore(models.Model):
|
||||||
|
"""
|
||||||
|
A model class to store all config variables in the database.
|
||||||
|
"""
|
||||||
|
|
||||||
|
key = models.CharField(max_length=255, unique=True, db_index=True)
|
||||||
|
"""A string, the key of the config variable."""
|
||||||
|
|
||||||
|
value = JSONField()
|
||||||
|
"""The value of the config variable. """
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
permissions = (('can_manage_config', ugettext_noop('Can manage configuration')),)
|
||||||
|
@ -3,7 +3,7 @@ from django.dispatch import Signal
|
|||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.utils.translation import ugettext_lazy
|
from django.utils.translation import ugettext_lazy
|
||||||
|
|
||||||
from openslides.config.api import ConfigVariable
|
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
|
||||||
@ -16,7 +16,7 @@ def setup_general_config(sender, **kwargs):
|
|||||||
Receiver function to setup general config variables for OpenSlides.
|
Receiver function to setup general config variables for OpenSlides.
|
||||||
There are two main groups: 'General' and 'Projector'. The group
|
There are two main groups: 'General' and 'Projector'. The group
|
||||||
'General' has subgroups. This function is connected to the signal
|
'General' has subgroups. This function is connected to the signal
|
||||||
openslides.config.signals.config_signal during app loading.
|
openslides.core.signals.config_signal during app loading.
|
||||||
"""
|
"""
|
||||||
# General Event
|
# General Event
|
||||||
|
|
||||||
@ -134,3 +134,7 @@ def setup_general_config(sender, **kwargs):
|
|||||||
weight=180,
|
weight=180,
|
||||||
group=ugettext_lazy('Projector'),
|
group=ugettext_lazy('Projector'),
|
||||||
translatable=True)
|
translatable=True)
|
||||||
|
|
||||||
|
|
||||||
|
config_signal = Signal(providing_args=[])
|
||||||
|
"""Signal to get all config tabs from all apps."""
|
||||||
|
@ -128,9 +128,9 @@ angular.module('OpenSlidesApp.core', [])
|
|||||||
|
|
||||||
.factory('Config', function(DS) {
|
.factory('Config', function(DS) {
|
||||||
return DS.defineResource({
|
return DS.defineResource({
|
||||||
name: 'config/config',
|
name: 'core/config',
|
||||||
idAttribute: 'key',
|
idAttribute: 'key',
|
||||||
endpoint: '/rest/config/config/'
|
endpoint: '/rest/core/config/'
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import re
|
import re
|
||||||
|
from collections import OrderedDict
|
||||||
|
from operator import attrgetter
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.staticfiles import finders
|
from django.contrib.staticfiles import finders
|
||||||
from django.core.urlresolvers import get_resolver
|
from django.core.urlresolvers import get_resolver
|
||||||
from django.http import HttpResponse
|
from django.http import Http404, HttpResponse
|
||||||
|
|
||||||
from openslides import __version__ as version
|
from openslides import __version__ as version
|
||||||
from openslides.utils import views as utils_views
|
from openslides.utils import views as utils_views
|
||||||
@ -16,10 +18,14 @@ from openslides.utils.rest_api import (
|
|||||||
ModelViewSet,
|
ModelViewSet,
|
||||||
ReadOnlyModelViewSet,
|
ReadOnlyModelViewSet,
|
||||||
Response,
|
Response,
|
||||||
|
SimpleMetadata,
|
||||||
ValidationError,
|
ValidationError,
|
||||||
|
ViewSet,
|
||||||
detail_route,
|
detail_route,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from .config import config
|
||||||
|
from .exceptions import ConfigError, ConfigNotFound
|
||||||
from .models import CustomSlide, Projector, Tag
|
from .models import CustomSlide, Projector, Tag
|
||||||
from .serializers import (
|
from .serializers import (
|
||||||
CustomSlideSerializer,
|
CustomSlideSerializer,
|
||||||
@ -227,3 +233,78 @@ class VersionView(utils_views.APIView):
|
|||||||
'description': get_plugin_description(plugin),
|
'description': get_plugin_description(plugin),
|
||||||
'version': get_plugin_version(plugin)})
|
'version': get_plugin_version(plugin)})
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigMetadata(SimpleMetadata):
|
||||||
|
"""
|
||||||
|
Custom metadata class to add config info to responses on OPTIONS requests.
|
||||||
|
"""
|
||||||
|
def determine_metadata(self, request, view):
|
||||||
|
# Sort config variables by weight.
|
||||||
|
config_variables = sorted(config.get_config_variables().values(), key=attrgetter('weight'))
|
||||||
|
|
||||||
|
# Build tree.
|
||||||
|
config_groups = []
|
||||||
|
for config_variable in config_variables:
|
||||||
|
if not config_groups or config_groups[-1]['name'] != config_variable.group:
|
||||||
|
config_groups.append(OrderedDict(
|
||||||
|
name=config_variable.group,
|
||||||
|
subgroups=[]))
|
||||||
|
if not config_groups[-1]['subgroups'] or config_groups[-1]['subgroups'][-1]['name'] != config_variable.subgroup:
|
||||||
|
config_groups[-1]['subgroups'].append(OrderedDict(
|
||||||
|
name=config_variable.subgroup,
|
||||||
|
items=[]))
|
||||||
|
config_groups[-1]['subgroups'][-1]['items'].append(config_variable.data)
|
||||||
|
|
||||||
|
# Add tree to metadata.
|
||||||
|
metadata = super().determine_metadata(request, view)
|
||||||
|
metadata['config_groups'] = config_groups
|
||||||
|
return metadata
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigViewSet(ViewSet):
|
||||||
|
"""
|
||||||
|
API endpoint to list, retrieve and update the config.
|
||||||
|
"""
|
||||||
|
metadata_class = ConfigMetadata
|
||||||
|
|
||||||
|
def list(self, request):
|
||||||
|
"""
|
||||||
|
Lists all config variables. Everybody can see them.
|
||||||
|
"""
|
||||||
|
return Response([{'key': key, 'value': value} for key, value in config.items()])
|
||||||
|
|
||||||
|
def retrieve(self, request, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Retrieves a config variable. Everybody can see it.
|
||||||
|
"""
|
||||||
|
key = kwargs['pk']
|
||||||
|
try:
|
||||||
|
value = config[key]
|
||||||
|
except ConfigNotFound:
|
||||||
|
raise Http404
|
||||||
|
return Response({'key': key, 'value': value})
|
||||||
|
|
||||||
|
def update(self, request, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Updates a config variable. Only managers can do this.
|
||||||
|
|
||||||
|
Example: {"value": 42}
|
||||||
|
"""
|
||||||
|
# Check permission.
|
||||||
|
if not request.user.has_perm('core.can_manage_config'):
|
||||||
|
self.permission_denied(request)
|
||||||
|
|
||||||
|
key = kwargs['pk']
|
||||||
|
value = request.data['value']
|
||||||
|
|
||||||
|
# Validate and change value.
|
||||||
|
try:
|
||||||
|
config[key] = value
|
||||||
|
except ConfigNotFound:
|
||||||
|
raise Http404
|
||||||
|
except ConfigError as e:
|
||||||
|
raise ValidationError({'detail': e})
|
||||||
|
|
||||||
|
# Return response.
|
||||||
|
return Response({'key': key, 'value': value})
|
||||||
|
@ -68,7 +68,7 @@ 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.config.middleware.ConfigCacheMiddleware',
|
'openslides.core.middleware.ConfigCacheMiddleware',
|
||||||
)
|
)
|
||||||
|
|
||||||
ROOT_URLCONF = 'openslides.urls'
|
ROOT_URLCONF = 'openslides.urls'
|
||||||
@ -90,7 +90,6 @@ INSTALLED_APPS = (
|
|||||||
'openslides.motions',
|
'openslides.motions',
|
||||||
'openslides.assignments',
|
'openslides.assignments',
|
||||||
'openslides.mediafiles',
|
'openslides.mediafiles',
|
||||||
'openslides.config',
|
|
||||||
)
|
)
|
||||||
|
|
||||||
TEMPLATE_CONTEXT_PROCESSORS = (
|
TEMPLATE_CONTEXT_PROCESSORS = (
|
||||||
|
@ -6,7 +6,7 @@ from django.utils.translation import ugettext_lazy, ugettext_noop
|
|||||||
|
|
||||||
from openslides.projector.models import SlideMixin
|
from openslides.projector.models import SlideMixin
|
||||||
from openslides.users.models import User
|
from openslides.users.models import User
|
||||||
from openslides.utils.rest_api import RESTModelMixin
|
from openslides.utils.models import RESTModelMixin
|
||||||
|
|
||||||
|
|
||||||
class Mediafile(RESTModelMixin, SlideMixin, models.Model):
|
class Mediafile(RESTModelMixin, SlideMixin, models.Model):
|
||||||
|
@ -12,7 +12,7 @@ class MotionAppConfig(AppConfig):
|
|||||||
from . import projector # noqa
|
from . import projector # noqa
|
||||||
|
|
||||||
# Import all required stuff.
|
# Import all required stuff.
|
||||||
from openslides.config.signals import config_signal
|
from openslides.core.signals import config_signal
|
||||||
from openslides.utils.rest_api import router
|
from openslides.utils.rest_api import router
|
||||||
from .signals import create_builtin_workflows, setup_motion_config
|
from .signals import create_builtin_workflows, setup_motion_config
|
||||||
from .views import CategoryViewSet, MotionViewSet, WorkflowViewSet
|
from .views import CategoryViewSet, MotionViewSet, WorkflowViewSet
|
||||||
|
@ -6,7 +6,7 @@ from django.utils.translation import ugettext as _
|
|||||||
from django.utils.translation import ugettext_lazy, ugettext_noop
|
from django.utils.translation import ugettext_lazy, ugettext_noop
|
||||||
from jsonfield import JSONField
|
from jsonfield import JSONField
|
||||||
|
|
||||||
from openslides.config.api import config
|
from openslides.core.config import config
|
||||||
from openslides.core.models import Tag
|
from openslides.core.models import Tag
|
||||||
from openslides.mediafiles.models import Mediafile
|
from openslides.mediafiles.models import Mediafile
|
||||||
from openslides.poll.models import (
|
from openslides.poll.models import (
|
||||||
@ -17,7 +17,7 @@ from openslides.poll.models import (
|
|||||||
)
|
)
|
||||||
from openslides.projector.models import SlideMixin
|
from openslides.projector.models import SlideMixin
|
||||||
from openslides.users.models import User
|
from openslides.users.models import User
|
||||||
from openslides.utils.rest_api import RESTModelMixin
|
from openslides.utils.models import RESTModelMixin
|
||||||
|
|
||||||
from .exceptions import WorkflowError
|
from .exceptions import WorkflowError
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ from reportlab.lib import colors
|
|||||||
from reportlab.lib.units import cm
|
from reportlab.lib.units import cm
|
||||||
from reportlab.platypus import PageBreak, Paragraph, Spacer, Table, TableStyle
|
from reportlab.platypus import PageBreak, Paragraph, Spacer, Table, TableStyle
|
||||||
|
|
||||||
from openslides.config.api import config
|
from openslides.core.config import config
|
||||||
from openslides.users.models import Group, User # TODO: remove this line
|
from openslides.users.models import Group, User # TODO: remove this line
|
||||||
from openslides.utils.pdf import stylesheet
|
from openslides.utils.pdf import stylesheet
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from openslides.config.api import config
|
from openslides.core.config import config
|
||||||
from openslides.utils.rest_api import (
|
from openslides.utils.rest_api import (
|
||||||
CharField,
|
CharField,
|
||||||
IntegerField,
|
IntegerField,
|
||||||
|
@ -2,7 +2,7 @@ from django.core.validators import MinValueValidator
|
|||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.utils.translation import pgettext, ugettext_lazy, ugettext_noop
|
from django.utils.translation import pgettext, ugettext_lazy, ugettext_noop
|
||||||
|
|
||||||
from openslides.config.api import ConfigVariable
|
from openslides.core.config import ConfigVariable
|
||||||
from openslides.poll.models import PERCENT_BASE_CHOICES
|
from openslides.poll.models import PERCENT_BASE_CHOICES
|
||||||
|
|
||||||
from .models import State, Workflow
|
from .models import State, Workflow
|
||||||
@ -13,7 +13,7 @@ def setup_motion_config(sender, **kwargs):
|
|||||||
Receiver function to setup all motion config variables. They are
|
Receiver function to setup all motion config variables. They are
|
||||||
grouped in 'General', 'Amendments', 'Supporters', 'Voting and ballot
|
grouped in 'General', 'Amendments', 'Supporters', 'Voting and ballot
|
||||||
papers' and 'PDF'. This function connected to the signal
|
papers' and 'PDF'. This function connected to the signal
|
||||||
openslides.config.signals.config_signal during app loading.
|
openslides.core.signals.config_signal during app loading.
|
||||||
"""
|
"""
|
||||||
# General
|
# General
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ from django.utils.translation import ugettext_noop
|
|||||||
from reportlab.platypus import SimpleDocTemplate
|
from reportlab.platypus import SimpleDocTemplate
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
|
|
||||||
from openslides.config.api import config
|
from openslides.core.config import config
|
||||||
from openslides.utils.rest_api import (
|
from openslides.utils.rest_api import (
|
||||||
ModelViewSet,
|
ModelViewSet,
|
||||||
Response,
|
Response,
|
||||||
|
@ -11,8 +11,7 @@ class UsersAppConfig(AppConfig):
|
|||||||
from . import projector # noqa
|
from . import projector # noqa
|
||||||
|
|
||||||
# Import all required stuff.
|
# Import all required stuff.
|
||||||
from openslides.config.signals import config_signal
|
from openslides.core.signals import config_signal, post_permission_creation
|
||||||
from openslides.core.signals import post_permission_creation
|
|
||||||
from openslides.utils.rest_api import router
|
from openslides.utils.rest_api import router
|
||||||
from .signals import create_builtin_groups_and_admin, setup_users_config
|
from .signals import create_builtin_groups_and_admin, setup_users_config
|
||||||
from .views import GroupViewSet, UserViewSet
|
from .views import GroupViewSet, UserViewSet
|
||||||
|
@ -8,7 +8,7 @@ from django.db.models import Q
|
|||||||
from django.utils.functional import SimpleLazyObject
|
from django.utils.functional import SimpleLazyObject
|
||||||
from rest_framework.authentication import BaseAuthentication
|
from rest_framework.authentication import BaseAuthentication
|
||||||
|
|
||||||
from openslides.config.api import config
|
from openslides.core.config import config
|
||||||
|
|
||||||
|
|
||||||
class AnonymousUser(DjangoAnonymousUser):
|
class AnonymousUser(DjangoAnonymousUser):
|
||||||
|
@ -13,9 +13,9 @@ from django.contrib.auth.models import ( # noqa
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy, ugettext_noop
|
from django.utils.translation import ugettext_lazy, ugettext_noop
|
||||||
|
|
||||||
from openslides.config.api import config
|
from openslides.core.config import config
|
||||||
from openslides.projector.models import SlideMixin
|
from openslides.projector.models import SlideMixin
|
||||||
from openslides.utils.rest_api import RESTModelMixin
|
from openslides.utils.models import RESTModelMixin
|
||||||
|
|
||||||
from .exceptions import UserError
|
from .exceptions import UserError
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ from reportlab.platypus import (
|
|||||||
TableStyle,
|
TableStyle,
|
||||||
)
|
)
|
||||||
|
|
||||||
from openslides.config.api import config
|
from openslides.core.config import config
|
||||||
from openslides.utils.pdf import stylesheet
|
from openslides.utils.pdf import stylesheet
|
||||||
|
|
||||||
from .models import User
|
from .models import User
|
||||||
|
@ -2,7 +2,7 @@ from django.db.models import Q
|
|||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.utils.translation import ugettext_lazy, ugettext_noop
|
from django.utils.translation import ugettext_lazy, ugettext_noop
|
||||||
|
|
||||||
from openslides.config.api import ConfigVariable
|
from openslides.core.config import ConfigVariable
|
||||||
|
|
||||||
from .models import Group, Permission, User
|
from .models import Group, Permission, User
|
||||||
|
|
||||||
@ -11,7 +11,7 @@ def setup_users_config(sender, **kwargs):
|
|||||||
"""
|
"""
|
||||||
Receiver function to setup all users config variables. They are grouped
|
Receiver function to setup all users config variables. They are grouped
|
||||||
in 'Sorting' and 'PDF'. This function is connected to the signal
|
in 'Sorting' and 'PDF'. This function is connected to the signal
|
||||||
openslides.config.signals.config_signal during app loading.
|
openslides.core.signals.config_signal during app loading.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Sorting
|
# Sorting
|
||||||
@ -110,7 +110,7 @@ def create_builtin_groups_and_admin(**kwargs):
|
|||||||
'assignments.can_nominate_other',
|
'assignments.can_nominate_other',
|
||||||
'assignments.can_nominate_self',
|
'assignments.can_nominate_self',
|
||||||
'assignments.can_see',
|
'assignments.can_see',
|
||||||
'config.can_manage',
|
'core.can_manage_config',
|
||||||
'core.can_manage_projector',
|
'core.can_manage_projector',
|
||||||
'core.can_manage_tags',
|
'core.can_manage_tags',
|
||||||
'core.can_see_dashboard',
|
'core.can_see_dashboard',
|
||||||
@ -171,7 +171,7 @@ def create_builtin_groups_and_admin(**kwargs):
|
|||||||
permission_dict['assignments.can_manage'],
|
permission_dict['assignments.can_manage'],
|
||||||
permission_dict['assignments.can_nominate_other'],
|
permission_dict['assignments.can_nominate_other'],
|
||||||
permission_dict['assignments.can_nominate_self'],
|
permission_dict['assignments.can_nominate_self'],
|
||||||
permission_dict['config.can_manage'],
|
permission_dict['core.can_manage_config'],
|
||||||
permission_dict['core.can_manage_projector'],
|
permission_dict['core.can_manage_projector'],
|
||||||
permission_dict['core.can_manage_tags'],
|
permission_dict['core.can_manage_tags'],
|
||||||
permission_dict['core.can_use_chat'],
|
permission_dict['core.can_use_chat'],
|
||||||
|
@ -299,7 +299,7 @@ def translate_customizable_strings(language_code):
|
|||||||
Translates all translatable config values and saves them into database.
|
Translates all translatable config values and saves them into database.
|
||||||
"""
|
"""
|
||||||
if check_for_language(language_code):
|
if check_for_language(language_code):
|
||||||
from openslides.config.api import config
|
from openslides.core.config import config
|
||||||
current_language = get_language()
|
current_language = get_language()
|
||||||
activate(language_code)
|
activate(language_code)
|
||||||
for name in config.get_all_translatable():
|
for name in config.get_all_translatable():
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from django.core.urlresolvers import reverse
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
@ -14,3 +15,27 @@ class MinMaxIntegerField(models.IntegerField):
|
|||||||
defaults = {'min_value': self.min_value, 'max_value': self.max_value}
|
defaults = {'min_value': self.min_value, 'max_value': self.max_value}
|
||||||
defaults.update(kwargs)
|
defaults.update(kwargs)
|
||||||
return super(MinMaxIntegerField, self).formfield(**defaults)
|
return super(MinMaxIntegerField, self).formfield(**defaults)
|
||||||
|
|
||||||
|
|
||||||
|
class RESTModelMixin:
|
||||||
|
"""
|
||||||
|
Mixin for django models which are used in our rest api.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get_root_rest_element(self):
|
||||||
|
"""
|
||||||
|
Returns the root rest instance.
|
||||||
|
|
||||||
|
Uses self as default.
|
||||||
|
"""
|
||||||
|
return self
|
||||||
|
|
||||||
|
def get_root_rest_url(self):
|
||||||
|
"""
|
||||||
|
Returns the detail url of the root model of this object.
|
||||||
|
"""
|
||||||
|
# Gets the default url-name in the same way as django rest framework
|
||||||
|
# does in relations.HyperlinkedModelSerializer
|
||||||
|
root_instance = self.get_root_rest_element()
|
||||||
|
rest_url = '%s-detail' % type(root_instance)._meta.object_name.lower()
|
||||||
|
return reverse(rest_url, args=[str(root_instance.pk)])
|
||||||
|
@ -11,7 +11,7 @@ from reportlab.pdfbase import pdfmetrics
|
|||||||
from reportlab.pdfbase.ttfonts import TTFont
|
from reportlab.pdfbase.ttfonts import TTFont
|
||||||
from reportlab.rl_config import defaultPageSize
|
from reportlab.rl_config import defaultPageSize
|
||||||
|
|
||||||
from openslides.config.api import config
|
from openslides.core.config import config
|
||||||
|
|
||||||
# register new truetype fonts
|
# register new truetype fonts
|
||||||
pdfmetrics.registerFont(TTFont(
|
pdfmetrics.registerFont(TTFont(
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import re
|
import re
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
from rest_framework.decorators import detail_route # noqa
|
from rest_framework.decorators import detail_route # noqa
|
||||||
from rest_framework.decorators import list_route # noqa
|
from rest_framework.decorators import list_route # noqa
|
||||||
from rest_framework.metadata import SimpleMetadata # noqa
|
from rest_framework.metadata import SimpleMetadata # noqa
|
||||||
@ -33,30 +32,6 @@ from .exceptions import OpenSlidesError
|
|||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
|
|
||||||
|
|
||||||
class RESTModelMixin:
|
|
||||||
"""
|
|
||||||
Mixin for django models which are used in our rest api.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def get_root_rest_element(self):
|
|
||||||
"""
|
|
||||||
Returns the root rest instance.
|
|
||||||
|
|
||||||
Uses self as default.
|
|
||||||
"""
|
|
||||||
return self
|
|
||||||
|
|
||||||
def get_root_rest_url(self):
|
|
||||||
"""
|
|
||||||
Returns the detail url of the root model of this object.
|
|
||||||
"""
|
|
||||||
# Gets the default url-name in the same way as django rest framework
|
|
||||||
# does in relations.HyperlinkedModelSerializer
|
|
||||||
root_instance = self.get_root_rest_element()
|
|
||||||
rest_url = '%s-detail' % type(root_instance)._meta.object_name.lower()
|
|
||||||
return reverse(rest_url, args=[str(root_instance.pk)])
|
|
||||||
|
|
||||||
|
|
||||||
class ModelViewSet(_ModelViewSet):
|
class ModelViewSet(_ModelViewSet):
|
||||||
"""
|
"""
|
||||||
Viewset for models. Before the method check_permission is called we
|
Viewset for models. Before the method check_permission is called we
|
||||||
|
@ -2,7 +2,7 @@ from django.core.management import call_command
|
|||||||
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.config.api import config
|
from openslides.core.config import config
|
||||||
|
|
||||||
|
|
||||||
class OpenSlidesDiscoverRunner(DiscoverRunner):
|
class OpenSlidesDiscoverRunner(DiscoverRunner):
|
||||||
|
@ -1,119 +0,0 @@
|
|||||||
from django.core.urlresolvers import reverse
|
|
||||||
from django.dispatch import receiver
|
|
||||||
from rest_framework import status
|
|
||||||
from rest_framework.test import APIClient
|
|
||||||
|
|
||||||
from openslides.config.api import ConfigVariable, config
|
|
||||||
from openslides.config.signals import config_signal
|
|
||||||
from openslides.utils.rest_api import ValidationError
|
|
||||||
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': "Wrong datatype. Expected <class 'int'>, got <class 'str'>."})
|
|
||||||
|
|
||||||
def test_update_good_choice(self):
|
|
||||||
self.client = APIClient()
|
|
||||||
self.client.login(username='admin', password='admin')
|
|
||||||
response = self.client.put(
|
|
||||||
reverse('config-detail', args=['test_var_wei0Rei9ahzooSohK1ph']),
|
|
||||||
{'value': 'key_2_yahb2ain1aeZ1lea1Pei'})
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
||||||
self.assertEqual(config['test_var_wei0Rei9ahzooSohK1ph'], 'key_2_yahb2ain1aeZ1lea1Pei')
|
|
||||||
|
|
||||||
def test_update_bad_choice(self):
|
|
||||||
self.client = APIClient()
|
|
||||||
self.client.login(username='admin', password='admin')
|
|
||||||
response = self.client.put(
|
|
||||||
reverse('config-detail', args=['test_var_wei0Rei9ahzooSohK1ph']),
|
|
||||||
{'value': 'test_value_bad_string'})
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
|
||||||
self.assertEqual(response.data, {'detail': 'Invalid input. Choice does not match.'})
|
|
||||||
|
|
||||||
def test_update_validator_ok(self):
|
|
||||||
self.client = APIClient()
|
|
||||||
self.client.login(username='admin', password='admin')
|
|
||||||
response = self.client.put(
|
|
||||||
reverse('config-detail', args=['test_var_Hi7Oje8Oith7goopeeng']),
|
|
||||||
{'value': 'valid_string'})
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
||||||
self.assertEqual(config['test_var_Hi7Oje8Oith7goopeeng'], 'valid_string')
|
|
||||||
|
|
||||||
def test_update_validator_invalid(self):
|
|
||||||
self.client = APIClient()
|
|
||||||
self.client.login(username='admin', password='admin')
|
|
||||||
response = self.client.put(
|
|
||||||
reverse('config-detail', args=['test_var_Hi7Oje8Oith7goopeeng']),
|
|
||||||
{'value': 'invalid_string'})
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
|
||||||
self.assertEqual(response.data, {'detail': 'Invalid input.'})
|
|
||||||
|
|
||||||
|
|
||||||
def validator_for_testing(value):
|
|
||||||
"""
|
|
||||||
Validator for testing.
|
|
||||||
"""
|
|
||||||
if value == 'invalid_string':
|
|
||||||
raise ValidationError({'detail': 'Invalid input.'})
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(config_signal, dispatch_uid='set_simple_config_view_integration_config_test')
|
|
||||||
def set_simple_config_view_integration_config_test(sender, **kwargs):
|
|
||||||
"""
|
|
||||||
Sets a simple config view with some config variables but without
|
|
||||||
grouping.
|
|
||||||
"""
|
|
||||||
yield ConfigVariable(
|
|
||||||
name='test_var_aeW3Quahkah1phahCheo',
|
|
||||||
default_value=None,
|
|
||||||
label='test_label_aeNahsheu8phahk8taYo')
|
|
||||||
|
|
||||||
yield ConfigVariable(
|
|
||||||
name='test_var_Xeiizi7ooH8Thuk5aida',
|
|
||||||
default_value='')
|
|
||||||
|
|
||||||
yield ConfigVariable(
|
|
||||||
name='test_var_ohhii4iavoh5Phoh5ahg',
|
|
||||||
default_value=0,
|
|
||||||
input_type='integer')
|
|
||||||
|
|
||||||
yield ConfigVariable(
|
|
||||||
name='test_var_wei0Rei9ahzooSohK1ph',
|
|
||||||
default_value='key_1_Queit2juchoocos2Vugh',
|
|
||||||
input_type='choice',
|
|
||||||
choices=(
|
|
||||||
{'value': 'key_1_Queit2juchoocos2Vugh', 'display_name': 'label_1_Queit2juchoocos2Vugh'},
|
|
||||||
{'value': 'key_2_yahb2ain1aeZ1lea1Pei', 'display_name': 'label_2_yahb2ain1aeZ1lea1Pei'}))
|
|
||||||
|
|
||||||
yield ConfigVariable(
|
|
||||||
name='test_var_Hi7Oje8Oith7goopeeng',
|
|
||||||
default_value='',
|
|
||||||
validators=(validator_for_testing,))
|
|
@ -1,10 +1,15 @@
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
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 openslides import __version__ as version
|
from openslides import __version__ as version
|
||||||
|
from openslides.core.config import ConfigVariable, config
|
||||||
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.test import TestCase
|
from openslides.utils.test import TestCase
|
||||||
|
|
||||||
|
|
||||||
@ -59,3 +64,113 @@ class VersionView(TestCase):
|
|||||||
{'verbose_name': 'Plugin tests.old.utils',
|
{'verbose_name': 'Plugin tests.old.utils',
|
||||||
'description': 'Description of plugin tests.old.utils',
|
'description': 'Description of plugin tests.old.utils',
|
||||||
'version': 'unknown'}]})
|
'version': 'unknown'}]})
|
||||||
|
|
||||||
|
|
||||||
|
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': "Wrong datatype. Expected <class 'int'>, got <class 'str'>."})
|
||||||
|
|
||||||
|
def test_update_good_choice(self):
|
||||||
|
self.client = APIClient()
|
||||||
|
self.client.login(username='admin', password='admin')
|
||||||
|
response = self.client.put(
|
||||||
|
reverse('config-detail', args=['test_var_wei0Rei9ahzooSohK1ph']),
|
||||||
|
{'value': 'key_2_yahb2ain1aeZ1lea1Pei'})
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(config['test_var_wei0Rei9ahzooSohK1ph'], 'key_2_yahb2ain1aeZ1lea1Pei')
|
||||||
|
|
||||||
|
def test_update_bad_choice(self):
|
||||||
|
self.client = APIClient()
|
||||||
|
self.client.login(username='admin', password='admin')
|
||||||
|
response = self.client.put(
|
||||||
|
reverse('config-detail', args=['test_var_wei0Rei9ahzooSohK1ph']),
|
||||||
|
{'value': 'test_value_bad_string'})
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertEqual(response.data, {'detail': 'Invalid input. Choice does not match.'})
|
||||||
|
|
||||||
|
def test_update_validator_ok(self):
|
||||||
|
self.client = APIClient()
|
||||||
|
self.client.login(username='admin', password='admin')
|
||||||
|
response = self.client.put(
|
||||||
|
reverse('config-detail', args=['test_var_Hi7Oje8Oith7goopeeng']),
|
||||||
|
{'value': 'valid_string'})
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(config['test_var_Hi7Oje8Oith7goopeeng'], 'valid_string')
|
||||||
|
|
||||||
|
def test_update_validator_invalid(self):
|
||||||
|
self.client = APIClient()
|
||||||
|
self.client.login(username='admin', password='admin')
|
||||||
|
response = self.client.put(
|
||||||
|
reverse('config-detail', args=['test_var_Hi7Oje8Oith7goopeeng']),
|
||||||
|
{'value': 'invalid_string'})
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertEqual(response.data, {'detail': 'Invalid input.'})
|
||||||
|
|
||||||
|
|
||||||
|
def validator_for_testing(value):
|
||||||
|
"""
|
||||||
|
Validator for testing.
|
||||||
|
"""
|
||||||
|
if value == 'invalid_string':
|
||||||
|
raise ValidationError({'detail': 'Invalid input.'})
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(config_signal, dispatch_uid='set_simple_config_view_integration_config_test')
|
||||||
|
def set_simple_config_view_integration_config_test(sender, **kwargs):
|
||||||
|
"""
|
||||||
|
Sets a simple config view with some config variables but without
|
||||||
|
grouping.
|
||||||
|
"""
|
||||||
|
yield ConfigVariable(
|
||||||
|
name='test_var_aeW3Quahkah1phahCheo',
|
||||||
|
default_value=None,
|
||||||
|
label='test_label_aeNahsheu8phahk8taYo')
|
||||||
|
|
||||||
|
yield ConfigVariable(
|
||||||
|
name='test_var_Xeiizi7ooH8Thuk5aida',
|
||||||
|
default_value='')
|
||||||
|
|
||||||
|
yield ConfigVariable(
|
||||||
|
name='test_var_ohhii4iavoh5Phoh5ahg',
|
||||||
|
default_value=0,
|
||||||
|
input_type='integer')
|
||||||
|
|
||||||
|
yield ConfigVariable(
|
||||||
|
name='test_var_wei0Rei9ahzooSohK1ph',
|
||||||
|
default_value='key_1_Queit2juchoocos2Vugh',
|
||||||
|
input_type='choice',
|
||||||
|
choices=(
|
||||||
|
{'value': 'key_1_Queit2juchoocos2Vugh', 'display_name': 'label_1_Queit2juchoocos2Vugh'},
|
||||||
|
{'value': 'key_2_yahb2ain1aeZ1lea1Pei', 'display_name': 'label_2_yahb2ain1aeZ1lea1Pei'}))
|
||||||
|
|
||||||
|
yield ConfigVariable(
|
||||||
|
name='test_var_Hi7Oje8Oith7goopeeng',
|
||||||
|
default_value='',
|
||||||
|
validators=(validator_for_testing,))
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from django.test.client import Client
|
from django.test.client import Client
|
||||||
|
|
||||||
from openslides.config.api import config
|
from openslides.core.config import config
|
||||||
from openslides.motions.models import Motion
|
from openslides.motions.models import Motion
|
||||||
from openslides.utils.test import TestCase
|
from openslides.utils.test import TestCase
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ from django.core.urlresolvers import reverse
|
|||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.test import APIClient
|
from rest_framework.test import APIClient
|
||||||
|
|
||||||
from openslides.config.api import config
|
from openslides.core.config import config
|
||||||
from openslides.core.models import Tag
|
from openslides.core.models import Tag
|
||||||
from openslides.motions.models import Category, Motion
|
from openslides.motions.models import Category, Motion
|
||||||
from openslides.utils.test import TestCase
|
from openslides.utils.test import TestCase
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
|
||||||
from openslides.config.api import ConfigVariable, config
|
from openslides.core.config import ConfigVariable, config
|
||||||
from openslides.config.exceptions import ConfigError, ConfigNotFound
|
from openslides.core.exceptions import ConfigError, ConfigNotFound
|
||||||
from openslides.config.signals import config_signal
|
from openslides.core.signals import config_signal
|
||||||
from openslides.utils.test import TestCase
|
from openslides.utils.test import TestCase
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from unittest import skip
|
from unittest import skip
|
||||||
|
|
||||||
from openslides.config.api import config
|
from openslides.core.config import config
|
||||||
from openslides.motions.exceptions import WorkflowError
|
from openslides.motions.exceptions import WorkflowError
|
||||||
from openslides.motions.models import Motion, State, Workflow
|
from openslides.motions.models import Motion, State, Workflow
|
||||||
from openslides.users.models import User
|
from openslides.users.models import User
|
||||||
|
@ -2,7 +2,7 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
from openslides.config.api import config
|
from openslides.core.config import config
|
||||||
from openslides.utils import main
|
from openslides.utils import main
|
||||||
from openslides.utils.test import TestCase
|
from openslides.utils.test import TestCase
|
||||||
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from openslides.config.api import ConfigVariable
|
from openslides.core.config import ConfigVariable
|
||||||
|
|
||||||
|
|
||||||
class TestConfigVariable(TestCase):
|
class TestConfigVariable(TestCase):
|
||||||
@patch('openslides.config.api.config', {'test_variable': None})
|
@patch('openslides.core.config.config', {'test_variable': None})
|
||||||
def test_default_value_in_data(self):
|
def test_default_value_in_data(self):
|
||||||
"""
|
"""
|
||||||
Tests, that the default_value attribute is in the 'data' property of
|
Tests, that the default_value attribute is in the 'data' property of
|
||||||
|
Loading…
Reference in New Issue
Block a user