Merge pull request #1579 from ostcar/config_into_core

Moved config app into the core app
This commit is contained in:
Oskar Hahn 2015-06-29 15:18:54 +02:00
commit db0a4224c2
50 changed files with 335 additions and 346 deletions

View File

@ -37,7 +37,7 @@ Other:
- Refactored projector API using metaclasses now.
- Renamed SignalConnectMetaClass classmethod get_all_objects to get_all
(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.
- Used AngularJS with additional libraries for single page frontend.
- Removed use of 'django.views.i18n.javascript_catalog'. Used angular-gettext

View File

@ -12,7 +12,7 @@ class AgendaAppConfig(AppConfig):
# Import all required stuff.
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 .signals import setup_agenda_config, listen_to_related_object_delete_signal
from .views import ItemViewSet

View File

@ -9,12 +9,12 @@ from django.db import models
from django.utils.translation import ugettext as _
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.projector.models import SlideMixin
from openslides.users.models import User
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

View File

@ -6,7 +6,7 @@ from django.core.validators import MaxLengthValidator, MinValueValidator
from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy
from openslides.config.api import ConfigVariable
from openslides.core.config import ConfigVariable
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
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.
yield ConfigVariable(

View File

@ -11,7 +11,7 @@ class AssignmentAppConfig(AppConfig):
from . import projector # noqa
# 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 .signals import setup_assignment_config
from .views import AssignmentViewSet, AssignmentPollViewSet

View File

@ -5,7 +5,7 @@ from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy, ugettext_noop
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.poll.models import (
BaseOption,
@ -17,7 +17,7 @@ from openslides.poll.models import (
from openslides.projector.models import SlideMixin
from openslides.users.models import User
from openslides.utils.exceptions import OpenSlidesError
from openslides.utils.rest_api import RESTModelMixin
from openslides.utils.models import RESTModelMixin
class AssignmentRelatedUser(RESTModelMixin, models.Model):

View File

@ -2,7 +2,7 @@ from django.core.validators import MinValueValidator
from django.utils.translation import ugettext as _
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
@ -10,7 +10,7 @@ def setup_assignment_config(sender, **kwargs):
"""
Receiver function to setup all assignment config variables. They are
grouped in 'Ballot and ballot papers' and 'PDF'. This function is
connected to the signal openslides.config.signals.config_signal during
connected to the signal openslides.core.signals.config_signal during
app loading.
"""
# Ballot and ballot papers

View File

@ -15,7 +15,7 @@ from reportlab.platypus import (
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.utils.pdf import stylesheet
from openslides.utils.rest_api import (

View File

@ -1 +0,0 @@
default_app_config = 'openslides.config.apps.ConfigAppConfig'

View File

@ -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')

View File

@ -1,9 +0,0 @@
from openslides.utils.exceptions import OpenSlidesError
class ConfigError(OpenSlidesError):
pass
class ConfigNotFound(ConfigError):
pass

View File

@ -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')),)

View File

@ -1,4 +0,0 @@
from django.dispatch import Signal
config_signal = Signal(providing_args=[])
"""Signal to get all config tabs from all apps."""

View File

@ -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})

View File

@ -12,11 +12,16 @@ class CoreAppConfig(AppConfig):
# Import all required stuff.
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.rest_api import router
from .signals import setup_general_config
from .views import CustomSlideViewSet, ProjectorViewSet, TagViewSet
from .views import (
ConfigViewSet,
CustomSlideViewSet,
ProjectorViewSet,
TagViewSet,
)
# Connect signals.
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/customslide', CustomSlideViewSet)
router.register('core/tag', TagViewSet)
router.register('core/config', ConfigViewSet, 'config')
# Update data when any model of any installed app is saved or deleted.
# TODO: Test if the m2m_changed signal is also needed.

View File

@ -3,7 +3,6 @@ from django.utils.translation import ugettext as _
from .exceptions import ConfigError, ConfigNotFound
from .models import ConfigStore
from .signals import config_signal
INPUT_TYPE_MAPPING = {
'string': str,
@ -97,6 +96,10 @@ class ConfigHandler:
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:

View File

@ -7,3 +7,11 @@ class ProjectorException(OpenSlidesError):
class TagException(OpenSlidesError):
pass
class ConfigError(OpenSlidesError):
pass
class ConfigNotFound(ConfigError):
pass

View File

@ -1,4 +1,4 @@
from openslides.config.api import config
from .config import config
class ConfigCacheMiddleware(object):

View File

@ -2,7 +2,6 @@ import jsonfield.fields
from django.db import migrations, models
import openslides.utils.models
import openslides.utils.rest_api
def add_default_projector(apps, schema_editor):
@ -32,20 +31,20 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='CustomSlide',
fields=[
('id', models.AutoField(serialize=False, primary_key=True, auto_created=True, verbose_name='ID')),
('title', models.CharField(verbose_name='Title', max_length=256)),
('text', models.TextField(verbose_name='Text', blank=True)),
('id', models.AutoField(auto_created=True, serialize=False, verbose_name='ID', primary_key=True)),
('title', models.CharField(max_length=256, verbose_name='Title')),
('text', models.TextField(blank=True, verbose_name='Text')),
('weight', models.IntegerField(verbose_name='Weight', default=0)),
],
options={
'ordering': ('weight', 'title'),
},
bases=(openslides.utils.rest_api.RESTModelMixin, models.Model),
bases=(openslides.utils.models.RESTModelMixin, models.Model),
),
migrations.CreateModel(
name='Projector',
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()),
],
options={
@ -55,21 +54,35 @@ class Migration(migrations.Migration):
('can_see_dashboard', 'Can see the dashboard'),
('can_use_chat', 'Can use the chat')),
},
bases=(openslides.utils.rest_api.RESTModelMixin, models.Model),
bases=(openslides.utils.models.RESTModelMixin, models.Model),
),
migrations.CreateModel(
name='Tag',
fields=[
('id', models.AutoField(serialize=False, primary_key=True, auto_created=True, verbose_name='ID')),
('name', models.CharField(verbose_name='Tag', unique=True, max_length=255)),
('id', models.AutoField(auto_created=True, serialize=False, verbose_name='ID', primary_key=True)),
('name', models.CharField(max_length=255, verbose_name='Tag', unique=True)),
],
options={
'permissions': (('can_manage_tags', 'Can manage tags'),),
'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(
add_default_projector,
code=add_default_projector,
reverse_code=None,
atomic=True,
),
]

View File

@ -3,8 +3,8 @@ from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy, ugettext_noop
from jsonfield import JSONField
from openslides.utils.models import RESTModelMixin
from openslides.utils.projector import ProjectorElement
from openslides.utils.rest_api import RESTModelMixin
from .exceptions import ProjectorException
@ -120,3 +120,18 @@ class Tag(RESTModelMixin, models.Model):
def __str__(self):
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')),)

View File

@ -3,7 +3,7 @@ from django.dispatch import Signal
from django.utils.translation import ugettext as _
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
# 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.
There are two main groups: 'General' and 'Projector'. The group
'General' has subgroups. This function is connected to the signal
openslides.config.signals.config_signal during app loading.
openslides.core.signals.config_signal during app loading.
"""
# General Event
@ -134,3 +134,7 @@ def setup_general_config(sender, **kwargs):
weight=180,
group=ugettext_lazy('Projector'),
translatable=True)
config_signal = Signal(providing_args=[])
"""Signal to get all config tabs from all apps."""

View File

@ -128,9 +128,9 @@ angular.module('OpenSlidesApp.core', [])
.factory('Config', function(DS) {
return DS.defineResource({
name: 'config/config',
name: 'core/config',
idAttribute: 'key',
endpoint: '/rest/config/config/'
endpoint: '/rest/core/config/'
});
})

View File

@ -1,9 +1,11 @@
import re
from collections import OrderedDict
from operator import attrgetter
from django.conf import settings
from django.contrib.staticfiles import finders
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.utils import views as utils_views
@ -16,10 +18,14 @@ from openslides.utils.rest_api import (
ModelViewSet,
ReadOnlyModelViewSet,
Response,
SimpleMetadata,
ValidationError,
ViewSet,
detail_route,
)
from .config import config
from .exceptions import ConfigError, ConfigNotFound
from .models import CustomSlide, Projector, Tag
from .serializers import (
CustomSlideSerializer,
@ -227,3 +233,78 @@ class VersionView(utils_views.APIView):
'description': get_plugin_description(plugin),
'version': get_plugin_version(plugin)})
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})

View File

@ -68,7 +68,7 @@ MIDDLEWARE_CLASSES = (
'django.middleware.csrf.CsrfViewMiddleware',
'openslides.users.auth.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'openslides.config.middleware.ConfigCacheMiddleware',
'openslides.core.middleware.ConfigCacheMiddleware',
)
ROOT_URLCONF = 'openslides.urls'
@ -90,7 +90,6 @@ INSTALLED_APPS = (
'openslides.motions',
'openslides.assignments',
'openslides.mediafiles',
'openslides.config',
)
TEMPLATE_CONTEXT_PROCESSORS = (

View File

@ -6,7 +6,7 @@ from django.utils.translation import ugettext_lazy, ugettext_noop
from openslides.projector.models import SlideMixin
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):

View File

@ -12,7 +12,7 @@ class MotionAppConfig(AppConfig):
from . import projector # noqa
# 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 .signals import create_builtin_workflows, setup_motion_config
from .views import CategoryViewSet, MotionViewSet, WorkflowViewSet

View File

@ -6,7 +6,7 @@ from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy, ugettext_noop
from jsonfield import JSONField
from openslides.config.api import config
from openslides.core.config import config
from openslides.core.models import Tag
from openslides.mediafiles.models import Mediafile
from openslides.poll.models import (
@ -17,7 +17,7 @@ from openslides.poll.models import (
)
from openslides.projector.models import SlideMixin
from openslides.users.models import User
from openslides.utils.rest_api import RESTModelMixin
from openslides.utils.models import RESTModelMixin
from .exceptions import WorkflowError

View File

@ -9,7 +9,7 @@ from reportlab.lib import colors
from reportlab.lib.units import cm
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.utils.pdf import stylesheet

View File

@ -1,7 +1,7 @@
from django.db import transaction
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 (
CharField,
IntegerField,

View File

@ -2,7 +2,7 @@ from django.core.validators import MinValueValidator
from django.utils.translation import ugettext as _
from django.utils.translation import pgettext, ugettext_lazy, ugettext_noop
from openslides.config.api import ConfigVariable
from openslides.core.config import ConfigVariable
from openslides.poll.models import PERCENT_BASE_CHOICES
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
grouped in 'General', 'Amendments', 'Supporters', 'Voting and ballot
papers' and 'PDF'. This function connected to the signal
openslides.config.signals.config_signal during app loading.
openslides.core.signals.config_signal during app loading.
"""
# General

View File

@ -6,7 +6,7 @@ from django.utils.translation import ugettext_noop
from reportlab.platypus import SimpleDocTemplate
from rest_framework import status
from openslides.config.api import config
from openslides.core.config import config
from openslides.utils.rest_api import (
ModelViewSet,
Response,

View File

@ -11,8 +11,7 @@ class UsersAppConfig(AppConfig):
from . import projector # noqa
# Import all required stuff.
from openslides.config.signals import config_signal
from openslides.core.signals import post_permission_creation
from openslides.core.signals import config_signal, post_permission_creation
from openslides.utils.rest_api import router
from .signals import create_builtin_groups_and_admin, setup_users_config
from .views import GroupViewSet, UserViewSet

View File

@ -8,7 +8,7 @@ from django.db.models import Q
from django.utils.functional import SimpleLazyObject
from rest_framework.authentication import BaseAuthentication
from openslides.config.api import config
from openslides.core.config import config
class AnonymousUser(DjangoAnonymousUser):

View File

@ -13,9 +13,9 @@ from django.contrib.auth.models import ( # noqa
from django.db import models
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.utils.rest_api import RESTModelMixin
from openslides.utils.models import RESTModelMixin
from .exceptions import UserError

View File

@ -14,7 +14,7 @@ from reportlab.platypus import (
TableStyle,
)
from openslides.config.api import config
from openslides.core.config import config
from openslides.utils.pdf import stylesheet
from .models import User

View File

@ -2,7 +2,7 @@ from django.db.models import Q
from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy, ugettext_noop
from openslides.config.api import ConfigVariable
from openslides.core.config import ConfigVariable
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
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
@ -110,7 +110,7 @@ def create_builtin_groups_and_admin(**kwargs):
'assignments.can_nominate_other',
'assignments.can_nominate_self',
'assignments.can_see',
'config.can_manage',
'core.can_manage_config',
'core.can_manage_projector',
'core.can_manage_tags',
'core.can_see_dashboard',
@ -171,7 +171,7 @@ def create_builtin_groups_and_admin(**kwargs):
permission_dict['assignments.can_manage'],
permission_dict['assignments.can_nominate_other'],
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_tags'],
permission_dict['core.can_use_chat'],

View File

@ -299,7 +299,7 @@ def translate_customizable_strings(language_code):
Translates all translatable config values and saves them into database.
"""
if check_for_language(language_code):
from openslides.config.api import config
from openslides.core.config import config
current_language = get_language()
activate(language_code)
for name in config.get_all_translatable():

View File

@ -1,3 +1,4 @@
from django.core.urlresolvers import reverse
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.update(kwargs)
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)])

View File

@ -11,7 +11,7 @@ from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.rl_config import defaultPageSize
from openslides.config.api import config
from openslides.core.config import config
# register new truetype fonts
pdfmetrics.registerFont(TTFont(

View File

@ -1,7 +1,6 @@
import re
from urllib.parse import urlparse
from django.core.urlresolvers import reverse
from rest_framework.decorators import detail_route # noqa
from rest_framework.decorators import list_route # noqa
from rest_framework.metadata import SimpleMetadata # noqa
@ -33,30 +32,6 @@ from .exceptions import OpenSlidesError
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):
"""
Viewset for models. Before the method check_permission is called we

View File

@ -2,7 +2,7 @@ from django.core.management import call_command
from django.test import TestCase as _TestCase
from django.test.runner import DiscoverRunner
from openslides.config.api import config
from openslides.core.config import config
class OpenSlidesDiscoverRunner(DiscoverRunner):

View File

@ -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,))

View File

@ -1,10 +1,15 @@
import json
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.models import CustomSlide, Projector
from openslides.core.signals import config_signal
from openslides.utils.rest_api import ValidationError
from openslides.utils.test import TestCase
@ -59,3 +64,113 @@ class VersionView(TestCase):
{'verbose_name': 'Plugin tests.old.utils',
'description': 'Description of plugin tests.old.utils',
'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,))

View File

@ -1,6 +1,6 @@
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.utils.test import TestCase

View File

@ -3,7 +3,7 @@ from django.core.urlresolvers import reverse
from rest_framework import status
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.motions.models import Category, Motion
from openslides.utils.test import TestCase

View File

@ -1,8 +1,8 @@
from django.dispatch import receiver
from openslides.config.api import ConfigVariable, config
from openslides.config.exceptions import ConfigError, ConfigNotFound
from openslides.config.signals import config_signal
from openslides.core.config import ConfigVariable, config
from openslides.core.exceptions import ConfigError, ConfigNotFound
from openslides.core.signals import config_signal
from openslides.utils.test import TestCase

View File

@ -1,6 +1,6 @@
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.models import Motion, State, Workflow
from openslides.users.models import User

View File

@ -2,7 +2,7 @@ import os
import sys
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.test import TestCase

View File

@ -1,11 +1,11 @@
from unittest import TestCase
from unittest.mock import patch
from openslides.config.api import ConfigVariable
from openslides.core.config import ConfigVariable
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):
"""
Tests, that the default_value attribute is in the 'data' property of