from django import forms from django.utils.translation import gettext as _ from django.utils.translation import ngettext from misago.admin.forms import YesNoSwitch __all__ = ["ChangeSettingsForm"] class ValidateChoicesNum(object): def __init__(self, min_choices=0, max_choices=0): self.min_choices = min_choices self.max_choices = max_choices def __call__(self, data): data_len = len(data) if self.min_choices and self.min_choices > data_len: message = ngettext( "You have to select at least %(choices)d option.", "You have to select at least %(choices)d options.", self.min_choices, ) raise forms.ValidationError(message % {"choices": self.min_choices}) if self.max_choices and self.max_choices < data_len: message = ngettext( "You cannot select more than %(choices)d option.", "You cannot select more than %(choices)d options.", self.max_choices, ) raise forms.ValidationError(message % {"choices": self.max_choices}) return data def basic_kwargs(setting, extra): kwargs = { "label": _(setting.name), "initial": setting.value, "required": extra.get("min_length") or extra.get("min"), } if setting.description: kwargs["help_text"] = _(setting.description) if setting.form_field == "yesno": # YesNoSwitch is int-base and setting is bool based # this means we need to do quick conversion kwargs["initial"] = 1 if kwargs["initial"] else 0 if kwargs["required"]: if kwargs.get("help_text"): format = {"help_text": kwargs["help_text"]} kwargs["help_text"] = _("Required. %(help_text)s") % format else: kwargs["help_text"] = _("This field is required.") return kwargs def localise_choices(extra): return [(v, _(l)) for v, l in extra.get("choices", [])] def create_checkbox(setting, kwargs, extra): kwargs["widget"] = forms.CheckboxSelectMultiple() kwargs["choices"] = localise_choices(extra) if extra.get("min") or extra.get("max"): kwargs["validators"] = [ ValidateChoicesNum(extra.pop("min", 0), extra.pop("max", 0)) ] if setting.python_type == "int": return forms.TypedMultipleChoiceField(coerce="int", **kwargs) else: return forms.MultipleChoiceField(**kwargs) def create_choice(setting, kwargs, extra): if setting.form_field == "choice": kwargs["widget"] = forms.RadioSelect() else: kwargs["widget"] = forms.Select() kwargs["choices"] = localise_choices(extra) if setting.python_type == "int": return forms.TypedChoiceField(coerce="int", **kwargs) else: return forms.ChoiceField(**kwargs) def create_text(setting, kwargs, extra): kwargs.update(extra) if setting.python_type == "int": return forms.IntegerField(**kwargs) else: return forms.CharField(**kwargs) def create_textarea(setting, kwargs, extra): widget_kwargs = {} if extra.get("min_length", 0) == 0: kwargs["required"] = False if extra.get("rows", 0): widget_kwargs["attrs"] = {"rows": extra.pop("rows")} kwargs["widget"] = forms.Textarea(**widget_kwargs) return forms.CharField(**kwargs) def create_yesno(setting, kwargs, extra): return YesNoSwitch(**kwargs) FIELD_STYPES = { "checkbox": create_checkbox, "radio": create_choice, "select": create_choice, "text": create_text, "textarea": create_textarea, "yesno": create_yesno, } def setting_field(FormType, setting): field_factory = FIELD_STYPES[setting.form_field] field_extra = setting.field_extra form_field = field_factory(setting, basic_kwargs(setting, field_extra), field_extra) FormType = type( "FormType%s" % setting.pk, (FormType,), {setting.setting: form_field} ) return FormType def ChangeSettingsForm(data=None, group=None): """factory method that builds valid form for settings group""" class FormType(forms.Form): pass fieldsets = [] fieldset_legend = None fieldset_form = FormType fieldset_fields = False for setting in group.setting_set.order_by("order"): if setting.legend and setting.legend != fieldset_legend: if fieldset_fields: fieldsets.append( {"legend": fieldset_legend, "form": fieldset_form(data)} ) fieldset_legend = setting.legend fieldset_form = FormType fieldset_fields = False fieldset_fields = True fieldset_form = setting_field(fieldset_form, setting) if fieldset_fields: fieldsets.append({"legend": fieldset_legend, "form": fieldset_form(data)}) return fieldsets