Source code for shuup.xtheme.plugins.forms

from collections import OrderedDict
from copy import deepcopy

import six
from django import forms
from django.conf import settings

from shuup.xtheme.plugins.consts import FALLBACK_LANGUAGE_CODE


[docs] class PluginForm(forms.Form): """ Base class for plugin configuration forms. """
[docs] def __init__(self, **kwargs): self.plugin = kwargs.pop("plugin") self.request = kwargs.pop("request") super().__init__(**kwargs) self.populate() self.set_defaults() self.init_translated_fields()
[docs] def populate(self): # pragma: no cover, doccov: ignore # Subclass hook (overriding __init__ all the time is such a bore) pass
[docs] def init_translated_fields(self): self.translatable_field_names = [] self.monolingual_field_names = [] languages = self.get_languages() new_fields = OrderedDict() for name, field in six.iteritems(self.fields): if isinstance(field, TranslatableField): self.translatable_field_names.append(name) for language_code in languages: key = f"{name}_{language_code}" new_fields[key] = deepcopy(field) new_fields[key].initial = self.plugin.get_translated_value(name, language=language_code) new_fields[key].required = False elif field: self.monolingual_field_names.append(name) new_fields[name] = field self.fields = new_fields
[docs] def set_defaults(self): """ Set the forms initial values based on plugin defaults Use the plugin's default configuration as the default form field initial values. """ for key, value in self.plugin.get_defaults().items(): if key in self.fields and self.fields[key].initial is None: self.fields[key].initial = value
[docs] def full_clean(self): """ Use initial values as defaults for cleaned data """ super().full_clean() for name in self.fields: if name in self.cleaned_data: continue if self.fields[name].initial is not None: self.cleaned_data[name] = self.fields[name].initial _ = self.cleaned_data
[docs] def get_config(self): """ Get the new `config` dict for a plugin. Called when the form is valid, akin to `django.forms.models.ModelForm.save`. The default implementation just augments the old config with the cleaned data for the form. :return: A new JSONable (!) config dict :rtype: dict """ config = self.plugin.config.copy() data = self.cleaned_data.copy() languages = self.get_languages() for field_name in self.translatable_field_names: data[field_name] = {} for language_code in languages: key = f"{field_name}_{language_code}" val = self.cleaned_data.get(key, "") if val not in ["", None]: data[field_name][language_code] = val del data[key] config.update(data) return config
[docs] def get_languages(self): default_language = settings.PARLER_DEFAULT_LANGUAGE_CODE languages = [language[0] for language in settings.LANGUAGES] if default_language in languages: languages.remove(default_language) return [default_language] + languages + [FALLBACK_LANGUAGE_CODE]
[docs] class GenericPluginForm(PluginForm): """ A generic form for Xtheme plugins; populates itself based on `fields` in the plugin class. """
[docs] def populate(self): # doccov: ignore fields = self.plugin.fields if hasattr(fields, "items"): # Quacks like a dict; that's fine too fields = fields.items() for name, field in fields: self.fields[name] = deepcopy(field) self.initial.update(self.plugin.config)
[docs] class TranslatableField(forms.Field): pass # used solely to flag fields as translatable