clean up the config app. Use jsonfield to save the config values.

Syncdb necessary.
This commit is contained in:
Oskar Hahn 2012-07-07 14:48:21 +02:00
parent 15621b7702
commit e01d456e7b
11 changed files with 321 additions and 90 deletions

View File

@ -10,19 +10,59 @@
:license: GNU GPL, see LICENSE for more details.
"""
from django.forms import Form, CharField, TextInput, BooleanField, IntegerField, ChoiceField, Textarea, Select
from django import forms
from utils.forms import CssClassMixin
from models import config
from utils.translation_ext import ugettext as _
class GeneralConfigForm(Form, CssClassMixin):
event_name = CharField(widget=TextInput(),label=_("Event name"), max_length=30)
event_description = CharField(widget=TextInput(),label=_("Short description of event"), max_length=100, required=False)
event_date = CharField(widget=TextInput(), required=False, label=_("Event date"))
event_location = CharField(widget=TextInput(), required=False, label=_("Event location"))
event_organizer = CharField(widget=TextInput(), required=False, label=_("Event organizer"))
system_enable_anonymous = BooleanField(required=False, label=_("Allow access for anonymous guest users") )
frontpage_title = CharField(widget=TextInput(), required=False, label=_("Title") )
frontpage_welcometext = CharField(widget=Textarea(), required=False, label=_("Welcome text") )
class GeneralConfigForm(forms.Form, CssClassMixin):
event_name = forms.CharField(
widget=forms.TextInput(),
label=_("Event name"),
max_length=30,
)
event_description = forms.CharField(
widget=forms.TextInput(),
label=_("Short description of event"),
required=False,
max_length=100,
)
event_date = forms.CharField(
widget=forms.TextInput(),
label=_("Event date"),
required=False,
)
event_location = forms.CharField(
widget=forms.TextInput(),
label=_("Event location"),
required=False,
)
event_organizer = forms.CharField(
widget=forms.TextInput(),
label=_("Event organizer"),
required=False,
)
system_enable_anonymous = forms.BooleanField(
label=_("Allow access for anonymous guest users"),
required=False,
)
frontpage_title = forms.CharField(
widget=forms.TextInput(),
label=_("Title"),
required=False,
)
frontpage_welcometext = forms.CharField(
widget=forms.Textarea(),
label=_("Welcome text"),
required=False,
)

View File

@ -9,21 +9,26 @@
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
from pickle import dumps, loads
import base64
from django.conf import settings
from django.core.urlresolvers import reverse
from django.db import models
from django.dispatch import receiver
from django.utils.importlib import import_module
from django.utils.translation import ugettext as _, ugettext_noop
from utils.translation_ext import ugettext as _
from openslides.utils.jsonfield import JSONField
from openslides.utils.signals import template_manipulation
from openslides.config.signals import default_config_value
import settings
class ConfigStore(models.Model):
"""
Stores the config values.
"""
key = models.CharField(max_length=100, primary_key=True)
value = models.TextField()
value = JSONField()
def __unicode__(self):
return self.key
@ -31,41 +36,23 @@ class ConfigStore(models.Model):
class Meta:
verbose_name = 'config'
permissions = (
('can_manage_config', _("Can manage configuration", fixstr=True)),
('can_manage_config', ugettext_noop("Can manage configuration")),
)
# TODO:
# I used base64 to save pickled Data, there has to be another way see:
# http://stackoverflow.com/questions/2524970/djangounicodedecodeerror-while-storing-pickled-data
class Config(object):
def load_config(self):
self.config = {}
for key, value in ConfigStore.objects.all().values_list():
self.config[key] = loads(base64.decodestring(str(value)))
"""
Access the config values via config[...]
"""
def __getitem__(self, key):
# Had to be deactivated, because in more than one thread the values have
# to be loaded on each request.
## try:
## self.config
## except AttributeError:
## self.load_config()
## try:
## return self.config[key]
## except KeyError:
## pass
try:
return loads(base64.decodestring(str(ConfigStore.objects.get(key=key).value)))
return ConfigStore.objects.get(key=key).value
except ConfigStore.DoesNotExist:
pass
for receiver, value in default_config_value.send(sender='config', key=key):
for receiver, value in default_config_value.send(sender='config',
key=key):
if value is not None:
# if settings.DEBUG:
# print 'Using default for %s' % key
return value
if settings.DEBUG:
print "No default value for: %s" % key
@ -76,22 +63,25 @@ class Config(object):
c = ConfigStore.objects.get(pk=key)
except ConfigStore.DoesNotExist:
c = ConfigStore(pk=key)
c.value = base64.encodestring(dumps(value))
c.value = value
c.save()
try:
self.config[key] = value
except AttributeError:
self.load_config()
self.config[key] = value
def __contains__(self, item):
return ConfigStore.objects.filter(key=item).exists()
config = Config()
@receiver(default_config_value, dispatch_uid="config_default_config")
def default_config(sender, key, **kwargs):
"""
Global default values.
"""
return {
'event_name': 'OpenSlides',
'event_description': _('Presentation system for agenda, applications and elections'),
'event_description':
_('Presentation system for agenda, applications and elections'),
'event_date': '',
'event_location': '',
'event_organizer': '',
@ -99,25 +89,23 @@ def default_config(sender, key, **kwargs):
'frontpage_title': _('Welcome'),
'frontpage_welcometext': _('Welcome to OpenSlides!'),
'show_help_text': True,
'help_text': _("Get professional support for OpenSlides on %s.") % "<a href='http://openslides.org/' target='_blank'>www.openslides.org</a>",
'help_text': _("Get professional support for OpenSlides on %s.") %
"<a href='http://openslides.org/' target='_blank'> \
www.openslides.org</a>",
'system_enable_anonymous': False,
}.get(key)
from django.dispatch import receiver
from django.core.urlresolvers import reverse
from django.utils.importlib import import_module
import settings
from openslides.utils.signals import template_manipulation
@receiver(template_manipulation, dispatch_uid="config_submenu")
def set_submenu(sender, request, context, **kwargs):
"""
Submenu for the config tab.
"""
if not request.path.startswith('/config/'):
return None
menu_links = [
(reverse('config_general'), _('General'), request.path == reverse('config_general') ),
(reverse('config_general'), _('General'),
request.path == reverse('config_general')),
]
for app in settings.INSTALLED_APPS:
@ -139,7 +127,8 @@ def set_submenu(sender, request, context, **kwargs):
)
menu_links.append (
(reverse('config_version'), _('Version'), request.path == reverse('config_version') )
(reverse('config_version'), _('Version'),
request.path == reverse('config_version'))
)
context.update({

View File

@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-
"""
openslides.config.signals
~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~
Defines Signals for the config.

View File

@ -10,14 +10,13 @@
:license: GNU GPL, see LICENSE for more details.
"""
from django.conf.urls.defaults import *
from django.conf import settings
from django.conf.urls.defaults import patterns, url
from django.utils.importlib import import_module
import settings
from openslides.config.views import GeneralConfig, VersionConfig
from views import GeneralConfig, VersionConfig
urlpatterns = patterns('config.views',
urlpatterns = patterns('',
url(r'^general/$',
GeneralConfig.as_view(),
name='config_general',

View File

@ -10,27 +10,26 @@
:license: GNU GPL, see LICENSE for more details.
"""
from django.shortcuts import redirect
from django.core.urlresolvers import reverse
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.models import Group, Permission
from django.utils.translation import ugettext as _
from django.template.loader import render_to_string
from django.core.urlresolvers import reverse
from django.utils.importlib import import_module
import settings
from django.utils.translation import ugettext as _
from openslides import get_version
from utils.utils import template, permission_required
from utils.views import FormView, TemplateView
from utils.template import Tab
from openslides.utils.template import Tab
from openslides.utils.views import FormView, TemplateView
from forms import GeneralConfigForm
from models import config
from openslides.config.forms import GeneralConfigForm
from openslides.config.models import config
class GeneralConfig(FormView):
"""
Gereral config values.
"""
permission_required = 'config.can_manage_config'
form_class = GeneralConfigForm
template_name = 'config/general.html'
@ -57,7 +56,8 @@ class GeneralConfig(FormView):
# frontpage
config['frontpage_title'] = form.cleaned_data['frontpage_title']
config['frontpage_welcometext'] = form.cleaned_data['frontpage_welcometext']
config['frontpage_welcometext'] = \
form.cleaned_data['frontpage_welcometext']
# system
if form.cleaned_data['system_enable_anonymous']:
@ -66,21 +66,29 @@ class GeneralConfig(FormView):
try:
anonymous = Group.objects.get(name='Anonymous')
except Group.DoesNotExist:
default_perms = [u'can_see_agenda', u'can_see_projector', u'can_see_application']
default_perms = [u'can_see_agenda', u'can_see_projector',
u'can_see_application']
anonymous = Group()
anonymous.name = 'Anonymous'
anonymous.save()
anonymous.permissions = Permission.objects.filter(codename__in=default_perms)
anonymous.permissions = Permission.objects.filter(
codename__in=default_perms)
anonymous.save()
messages.success(self.request, _('Anonymous access enabled. Please modify the "Anonymous" group to fit your required permissions.'))
messages.success(self.request,
_('Anonymous access enabled. Please modify the "Anonymous" \
group to fit your required permissions.'))
else:
config['system_enable_anonymous'] = False
messages.success(self.request, _('General settings successfully saved.'))
messages.success(self.request,
_('General settings successfully saved.'))
return super(GeneralConfig, self).form_valid(form)
class VersionConfig(TemplateView):
"""
Show version infos.
"""
permission_required = 'config.can_manage_config'
template_name = 'config/version.html'
@ -99,12 +107,14 @@ class VersionConfig(TemplateView):
plugin_name = mod.__name__.split('.')[0]
context['versions'].append((plugin_name, plugin_version))
return context
def register_tab(request):
selected = True if request.path.startswith('/config/') else False
"""
Register the config tab.
"""
selected = request.path.startswith('/config/')
return Tab(
title=_('Configuration'),
url=reverse('config_general'),

View File

@ -61,7 +61,7 @@ def main(argv = None):
if opts.reset_admin:
create_or_reset_admin_user()
# NOTE: --insecure is needed so static files will be served if
# NOTE: --insecure is needed so static files will be served if
# DEBUG is set to False
argv = ["", "runserver", "--noreload", "--insecure"]
if opts.nothread:
@ -186,13 +186,8 @@ def set_system_url(url):
from openslides.config.models import config
key = "participant_pdf_system_url"
try:
if key in config.config:
return
except AttributeError:
config.load_config()
if key in config.config:
return
if key in config:
return
config[key] = url

View File

@ -0,0 +1,13 @@
https://github.com/bradjasper/django-jsonfield
django-jsonfield is a reusable django field that allows you to store validated JSON in your model.
It silently takes care of serialization. To use, simple add the field to one of your models.
===
from django.db import models
from jsonfield import JSONField
class MyModel(models.Model):
json = JSONField()

View File

@ -0,0 +1 @@
from fields import JSONField

View File

@ -0,0 +1,75 @@
from django.db import models
from django.core.serializers.json import DjangoJSONEncoder
from django.utils import simplejson as json
from django.utils.translation import ugettext_lazy as _
from django.forms.fields import Field
from django.forms.util import ValidationError as FormValidationError
class JSONFormField(Field):
def clean(self, value):
if not value and not self.required:
return None
value = super(JSONFormField, self).clean(value)
if isinstance(value, basestring):
try:
json.loads(value)
except ValueError:
raise FormValidationError(_("Enter valid JSON"))
return value
class JSONField(models.TextField):
"""JSONField is a generic textfield that serializes/unserializes JSON objects"""
# Used so to_python() is called
__metaclass__ = models.SubfieldBase
def __init__(self, *args, **kwargs):
self.dump_kwargs = kwargs.pop('dump_kwargs', {'cls': DjangoJSONEncoder})
self.load_kwargs = kwargs.pop('load_kwargs', {})
super(JSONField, self).__init__(*args, **kwargs)
def to_python(self, value):
"""Convert string value to JSON"""
if isinstance(value, basestring):
try:
return json.loads(value, **self.load_kwargs)
except ValueError:
pass
return value
def get_db_prep_value(self, value, connection, prepared=False):
"""Convert JSON object to a string"""
if isinstance(value, basestring):
return value
return json.dumps(value, **self.dump_kwargs)
def value_to_string(self, obj):
value = self._get_val_from_obj(obj)
return self.get_prep_value(value)
def value_from_object(self, obj):
return json.dumps(super(JSONField, self).value_from_object(obj))
def formfield(self, **kwargs):
if "form_class" not in kwargs:
kwargs["form_class"] = JSONFormField
field = super(JSONField, self).formfield(**kwargs)
if not field.help_text:
field.help_text = "Enter valid JSON"
return field
try:
from south.modelsinspector import add_introspection_rules
add_introspection_rules([], ["^jsonfield\.fields\.JSONField"])
except ImportError:
pass

View File

@ -0,0 +1 @@
# Django needs this to see it as a project

View File

@ -0,0 +1,108 @@
from django.db import models
from django.test import TestCase
from django.utils import simplejson as json
from fields import JSONField
class JsonModel(models.Model):
json = JSONField()
class ComplexEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, complex):
return {
'__complex__': True,
'real': obj.real,
'imag': obj.imag,
}
return json.JSONEncoder.default(self, obj)
def as_complex(dct):
if '__complex__' in dct:
return complex(dct['real'], dct['imag'])
return dct
class JSONModelCustomEncoders(models.Model):
# A JSON field that can store complex numbers
json = JSONField(
dump_kwargs={'cls': ComplexEncoder},
load_kwargs={'object_hook': as_complex},
)
class JSONFieldTest(TestCase):
"""JSONField Wrapper Tests"""
def test_json_field_create(self):
"""Test saving a JSON object in our JSONField"""
json_obj = {
"item_1": "this is a json blah",
"blergh": "hey, hey, hey"}
obj = JsonModel.objects.create(json=json_obj)
new_obj = JsonModel.objects.get(id=obj.id)
self.failUnlessEqual(new_obj.json, json_obj)
def test_json_field_modify(self):
"""Test modifying a JSON object in our JSONField"""
json_obj_1 = {'a': 1, 'b': 2}
json_obj_2 = {'a': 3, 'b': 4}
obj = JsonModel.objects.create(json=json_obj_1)
self.failUnlessEqual(obj.json, json_obj_1)
obj.json = json_obj_2
self.failUnlessEqual(obj.json, json_obj_2)
obj.save()
self.failUnlessEqual(obj.json, json_obj_2)
self.assert_(obj)
def test_json_field_load(self):
"""Test loading a JSON object from the DB"""
json_obj_1 = {'a': 1, 'b': 2}
obj = JsonModel.objects.create(json=json_obj_1)
new_obj = JsonModel.objects.get(id=obj.id)
self.failUnlessEqual(new_obj.json, json_obj_1)
def test_json_list(self):
"""Test storing a JSON list"""
json_obj = ["my", "list", "of", 1, "objs", {"hello": "there"}]
obj = JsonModel.objects.create(json=json_obj)
new_obj = JsonModel.objects.get(id=obj.id)
self.failUnlessEqual(new_obj.json, json_obj)
def test_empty_objects(self):
"""Test storing empty objects"""
for json_obj in [{}, [], 0, '', False]:
obj = JsonModel.objects.create(json=json_obj)
new_obj = JsonModel.objects.get(id=obj.id)
self.failUnlessEqual(json_obj, obj.json)
self.failUnlessEqual(json_obj, new_obj.json)
def test_custom_encoder(self):
"""Test encoder_cls and object_hook"""
value = 1 + 3j # A complex number
obj = JSONModelCustomEncoders.objects.create(json=value)
new_obj = JSONModelCustomEncoders.objects.get(pk=obj.pk)
self.failUnlessEqual(value, new_obj.json)