2015-04-26 13:53:00 +00:00
|
|
|
import math
|
|
|
|
from copy import deepcopy
|
|
|
|
from functools import partial
|
|
|
|
|
|
|
|
from django import forms
|
|
|
|
from django.core.exceptions import ValidationError
|
|
|
|
from django.forms.formsets import formset_factory
|
2015-04-27 12:24:17 +00:00
|
|
|
from django.utils.functional import Promise
|
2015-04-26 13:53:00 +00:00
|
|
|
from django.utils.translation import ugettext_lazy as _
|
|
|
|
|
|
|
|
from orchestra.forms import ReadOnlyFormMixin, widgets
|
2015-04-27 12:24:17 +00:00
|
|
|
from orchestra.utils.python import format_exception
|
2015-04-26 13:53:00 +00:00
|
|
|
|
|
|
|
from . import parser
|
|
|
|
|
|
|
|
from django import forms
|
|
|
|
from django.utils.safestring import mark_safe
|
|
|
|
|
|
|
|
|
|
|
|
class SettingForm(ReadOnlyFormMixin, forms.Form):
|
|
|
|
TEXTAREA = partial(forms.CharField,
|
|
|
|
widget=forms.Textarea(attrs={
|
|
|
|
'cols': 65,
|
|
|
|
'rows': 2,
|
2015-04-27 12:24:17 +00:00
|
|
|
'style': 'font-family: monospace',
|
2015-04-26 13:53:00 +00:00
|
|
|
}))
|
|
|
|
CHARFIELD = partial(forms.CharField,
|
2015-04-27 12:24:17 +00:00
|
|
|
widget=forms.TextInput(attrs={
|
|
|
|
'size': 65,
|
|
|
|
'style': 'font-family: monospace',
|
|
|
|
}))
|
|
|
|
NON_EDITABLE = partial(forms.CharField, widget=widgets.SpanWidget, required=False)
|
2015-04-26 13:53:00 +00:00
|
|
|
FORMFIELD_FOR_SETTING_TYPE = {
|
|
|
|
bool: partial(forms.BooleanField, required=False),
|
|
|
|
int: forms.IntegerField,
|
|
|
|
tuple: TEXTAREA,
|
|
|
|
list: TEXTAREA,
|
|
|
|
dict: TEXTAREA,
|
2015-04-27 12:24:17 +00:00
|
|
|
Promise: CHARFIELD,
|
2015-04-26 13:53:00 +00:00
|
|
|
str: CHARFIELD,
|
|
|
|
}
|
|
|
|
|
|
|
|
name = forms.CharField(label=_("name"))
|
|
|
|
default = forms.CharField(label=_("default"))
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
readonly_fields = ('name', 'default')
|
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
initial = kwargs.get('initial')
|
|
|
|
if initial:
|
|
|
|
self.setting_type = initial['type']
|
2015-04-26 15:52:40 +00:00
|
|
|
self.setting = initial['setting']
|
|
|
|
setting = self.setting
|
2015-04-27 12:24:17 +00:00
|
|
|
if setting.serializable:
|
|
|
|
serialized_value = parser.serialize(initial['value'])
|
|
|
|
serialized_default = parser.serialize(initial['default'])
|
|
|
|
else:
|
|
|
|
serialized_value = parser.NotSupported()
|
|
|
|
serialized_default = parser.NotSupported()
|
2015-04-26 15:52:40 +00:00
|
|
|
if not setting.editable or isinstance(serialized_value, parser.NotSupported):
|
2015-04-26 13:53:00 +00:00
|
|
|
field = self.NON_EDITABLE
|
|
|
|
else:
|
2015-04-26 15:52:40 +00:00
|
|
|
choices = setting.choices
|
2015-04-26 13:53:00 +00:00
|
|
|
field = forms.ChoiceField
|
2015-04-26 15:52:40 +00:00
|
|
|
multiple = setting.multiple
|
2015-04-26 13:53:00 +00:00
|
|
|
if multiple:
|
|
|
|
field = partial(forms.MultipleChoiceField, widget=forms.CheckboxSelectMultiple)
|
|
|
|
if choices:
|
|
|
|
# Lazy loading
|
|
|
|
if callable(choices):
|
|
|
|
choices = choices()
|
|
|
|
if not multiple:
|
|
|
|
choices = tuple((parser.serialize(val), verb) for val, verb in choices)
|
|
|
|
field = partial(field, choices=choices)
|
|
|
|
else:
|
|
|
|
field = self.FORMFIELD_FOR_SETTING_TYPE.get(self.setting_type, self.NON_EDITABLE)
|
|
|
|
field = deepcopy(field)
|
|
|
|
real_field = field
|
|
|
|
while isinstance(real_field, partial):
|
|
|
|
real_field = real_field.func
|
|
|
|
# Do not serialize following form types
|
2015-04-26 15:52:40 +00:00
|
|
|
value = initial['value']
|
|
|
|
default = initial['default']
|
|
|
|
self.changed = bool(value != default)
|
2015-04-26 13:53:00 +00:00
|
|
|
if real_field not in (forms.MultipleChoiceField, forms.BooleanField):
|
|
|
|
value = serialized_value
|
|
|
|
default = serialized_default
|
|
|
|
initial['value'] = value
|
|
|
|
initial['default'] = default
|
|
|
|
super(SettingForm, self).__init__(*args, **kwargs)
|
|
|
|
if initial:
|
|
|
|
self.fields['value'] = field(label=_("value"))
|
|
|
|
if isinstance(self.fields['value'].widget, forms.Textarea):
|
|
|
|
rows = math.ceil(len(value)/65)
|
|
|
|
self.fields['value'].widget.attrs['rows'] = rows
|
2015-04-26 15:52:40 +00:00
|
|
|
self.fields['name'].help_text = mark_safe(setting.help_text)
|
2015-04-26 13:53:00 +00:00
|
|
|
self.fields['name'].widget.attrs['readonly'] = True
|
|
|
|
self.app = initial['app']
|
|
|
|
|
|
|
|
def clean_value(self):
|
|
|
|
value = self.cleaned_data['value']
|
|
|
|
if not value:
|
|
|
|
return parser.NotSupported()
|
|
|
|
if not isinstance(value, str):
|
|
|
|
value = parser.serialize(value)
|
|
|
|
try:
|
|
|
|
value = eval(value, parser.get_eval_context())
|
|
|
|
except Exception as exc:
|
2015-04-27 12:24:17 +00:00
|
|
|
raise ValidationError(format_exception(exc))
|
2015-04-26 15:52:40 +00:00
|
|
|
self.setting.validate_value(value)
|
2015-04-26 13:53:00 +00:00
|
|
|
if not isinstance(value, self.setting_type):
|
|
|
|
if self.setting_type in (tuple, list) and isinstance(value, (tuple, list)):
|
|
|
|
value = self.setting_type(value)
|
|
|
|
return value
|
|
|
|
|
|
|
|
|
|
|
|
SettingFormSet = formset_factory(SettingForm, extra=0)
|