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)