Source code for shuup.notify.admin_module.forms
# -*- coding: utf-8 -*-
# This file is part of Shuup.
#
# Copyright (c) 2012-2021, Shuup Commerce Inc. All rights reserved.
#
# This source code is licensed under the OSL-3.0 license found in the
# LICENSE file in the root directory of this source tree.
from __future__ import unicode_literals
import copy
from collections import OrderedDict, defaultdict
from django import forms
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from shuup.notify.admin_module.utils import get_name_map
from shuup.notify.enums import UNILINGUAL_TEMPLATE_LANGUAGE, TemplateUse
from shuup.notify.models import Script
from shuup.utils.i18n import get_language_name
[docs]class ScriptForm(forms.ModelForm):
event_identifier = forms.ChoiceField(label=_("Event"), help_text=_("Choose which event to bind this script to."))
name = forms.CharField(label=_("Script Name"), help_text=_("Type in a descriptive name for your new script."))
enabled = forms.BooleanField(
label=_("Enable Script"),
help_text=_("Choose whether this script should be activated when its event fires."),
required=False,
)
def __init__(self, **kwargs):
self.shop = kwargs.pop("shop", None)
super(ScriptForm, self).__init__(**kwargs)
event_choices = get_name_map("notify_event")
self.fields["event_identifier"].choices = event_choices
self.fields["event_identifier"].widget.choices = event_choices
if self.instance.pk:
self.fields["event_identifier"].help_text = _(
"Warning! Changing the event for an existing script may have unexpected effects."
)
[docs] def save(self, commit=True):
self.instance.shop = self.shop
return super(ScriptForm, self).save(commit)
[docs]class ScriptItemEditForm(forms.Form):
def __init__(self, *args, **kwargs):
self.script_item = kwargs.pop("script_item")
self.event_class = kwargs.pop("event_class")
super(ScriptItemEditForm, self).__init__(*args, **kwargs)
self.variables = self.event_class.variables.copy()
self.populate_form()
[docs] def populate_form(self):
self.binding_field_info = OrderedDict()
self.template_field_info = defaultdict(OrderedDict)
self.template_languages = []
for identifier, binding in sorted(self.script_item.bindings.items(), key=lambda ib: ib[1].position):
self._populate_binding_fields(identifier, binding)
self._populate_template_fields()
def _populate_template_fields(self):
template_use = getattr(self.script_item, "template_use", TemplateUse.NONE)
if template_use == TemplateUse.MULTILINGUAL:
self.template_languages = []
# TODO: Should we get this list from somewhere else?
for language_code, language_name in settings.LANGUAGES:
self.template_languages.append((language_code, get_language_name(language_code)))
elif template_use == TemplateUse.UNILINGUAL:
self.template_languages = [(UNILINGUAL_TEMPLATE_LANGUAGE, _("Template"))]
else: # Nothing to do
return
fields = self.script_item.template_fields.items()
for lang_code, lang_name in self.template_languages:
for t_field_name, base_field in fields:
field = copy.deepcopy(base_field)
field.label = "%s (%s)" % (field.label, lang_name)
if lang_code == settings.PARLER_DEFAULT_LANGUAGE_CODE: # Only default language is required
field.required = getattr(base_field, "required", False)
else:
field.required = False
field_name = "t_%s_%s" % (lang_code, t_field_name)
self.fields[field_name] = field
self.template_field_info[lang_code][t_field_name] = field_name
def _populate_binding_fields(self, binding_identifier, binding):
"""
:param binding_identifier: Binding identifier.
:type binding_identifier: str
:param binding: Binding object.
:type binding: Binding
"""
binding_field_info = self.binding_field_info.setdefault(binding_identifier, {"binding": binding})
if binding.allow_constant:
field_name = "b_%s_c" % binding_identifier
self.fields[field_name] = binding.type.get_field(
label="Constant", required=(binding.required and not binding.allow_variable), initial=binding.default
)
binding_field_info["constant"] = field_name
if binding.allow_variable:
variables = [
(var_identifier, var.name)
for (var_identifier, var) in self.variables.items()
if binding.accepts_any_type or binding.type.is_coercible_from(var.type)
]
if variables:
choices = [("", "---------")] + variables
field_name = "b_%s_v" % binding_identifier
self.fields[field_name] = forms.ChoiceField(
choices=choices,
label=_("Bind to Variable"),
required=(binding.required and not binding.allow_constant),
)
binding_field_info["variable"] = field_name
# TODO: Maybe show a disabled field instead of nothing?
[docs] def get_initial(self):
initial = {}
for identifier, binding in self.script_item.bindings.items():
bind_data = self.script_item.data.get(identifier, {})
field_info = self.binding_field_info.get(identifier)
if not field_info:
return
for f in ("constant", "variable"):
if field_info.get(f):
initial[field_info[f]] = bind_data.get(f)
template_data = self.script_item.data.get("template_data", {})
for lang_code, field_info in self.template_field_info.items():
lang_templates = template_data.get(lang_code, {})
for t_field_name, field_name in field_info.items():
if lang_templates.get(t_field_name):
initial[field_name] = lang_templates.get(t_field_name)
return initial
def _save_binding(self, new_data, identifier, binding): # noqa (C901)
field_info = self.binding_field_info.get(identifier)
if not field_info:
return
if binding.allow_variable and "variable" in field_info:
variable_name = self.cleaned_data.get(field_info["variable"])
if variable_name:
new_data[identifier] = {"variable": variable_name}
return
if binding.allow_constant and "constant" in field_info:
constant_value = self.cleaned_data.get(field_info["constant"])
if constant_value:
if hasattr(constant_value, "value"): # Might be an enum TODO: fixme
constant_value = constant_value.value
if hasattr(constant_value, "pk"): # Might be a model instance TODO: fixme
constant_value = constant_value.pk
new_data[identifier] = {"constant": constant_value}
return
if binding.required:
message = "Error! Binding %s is required, but has no value." % binding.name
if field_info.get("constant"):
self.add_error(field_info["constant"], message)
if field_info.get("variable"):
self.add_error(field_info["variable"], message)
def _save_bindings(self, new_data):
for identifier, binding in self.script_item.bindings.items():
self._save_binding(new_data, identifier, binding)
def _save_template(self, new_data):
template_data = {}
for lang_code, field_info in self.template_field_info.items():
t_field_name_to_field_name = dict(field_info.items())
lang_vals = dict(
(t_field_name, (self.cleaned_data.get(field_name) or "").strip())
for (t_field_name, field_name) in field_info.items()
)
if not any(lang_vals.values()): # Not worth saving
continue
can_save = True
if lang_code == settings.PARLER_DEFAULT_LANGUAGE_CODE:
for t_field_name, content in lang_vals.items():
actual_field_name = t_field_name_to_field_name[t_field_name]
if self.fields[actual_field_name].required and not content: # Add error only to default languages
self.add_error(field_info[t_field_name], _("This field is missing content."))
can_save = False
if can_save:
template_data[lang_code] = lang_vals
new_data["template_data"] = template_data
[docs] def save(self):
new_data = {}
self._save_bindings(new_data)
self._save_template(new_data)
if self.errors:
raise forms.ValidationError("Error! There are errors.")
self.script_item.data = new_data
return self.script_item