You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

412 lines
15 KiB

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import re
from django import forms
from django.contrib.admin.widgets import (AdminDateWidget,
FilteredSelectMultiple)
from django.core.mail import EmailMultiAlternatives
from django.core.urlresolvers import reverse
from django.template.defaultfilters import slugify
from django.template.loader import render_to_string
from django.utils.translation import ugettext_lazy as _
from ipware.ip import get_ip
from unidecode import unidecode
from .fields import FormBuilderFileField, MultipleChoiceAutoCompleteField
from .models import Form, FormDefinition, FormField, FormSubmission
from .utils import int_to_hashid
from .widgets import DateInput, TelephoneInput, TimeInput
class FormFieldInlineForm(forms.ModelForm):
def clean(self):
cleaned_data = super(FormFieldInlineForm, self).clean()
requires_choice_values = ['checkbox_multiple', 'select', 'radio']
if (cleaned_data.get('field_type') in requires_choice_values and
not cleaned_data.get('choice_values')):
error_msg = _('This field is required.')
self._errors['choice_values'] = self.error_class([error_msg])
return cleaned_data
class Meta:
model = FormField
fields = '__all__'
class FormDefinitionAdminForm(forms.ModelForm):
def clean(self):
cleaned_data = super(FormDefinitionAdminForm, self).clean()
populated_count = 0
storage_fields = ('email_to', 'save_data', )
for field in storage_fields:
if cleaned_data.get(field, None):
populated_count += 1
if not populated_count:
error_msg = _(
'You must choose a storage option for this Form. '
'You can choose to use multiple storage options if you prefer. ')
for field in storage_fields:
self._errors[field] = self.error_class([error_msg])
return cleaned_data
class Meta:
model = FormDefinition
fields = '__all__'
class FormBuilder(forms.Form):
error_css_class = 'error'
required_css_class = 'required'
form_id = forms.CharField(widget=forms.HiddenInput)
referrer = forms.CharField(widget=forms.HiddenInput)
def __init__(self, form_definition, *args, **kwargs):
super(FormBuilder, self).__init__(*args, **kwargs)
self.form_definition = form_definition
self.field_names = []
self.file_fields = []
self.field_types = {}
self.submission_url = reverse('djangocms_forms_submissions')
self.fields['form_id'].initial = int_to_hashid(form_definition.pk)
self.redirect_url = form_definition.redirect_url
for field in form_definition.fields.all():
if hasattr(self, 'prepare_%s' % field.field_type):
field_name = self.get_unique_field_name(field)
form_field = getattr(self, 'prepare_%s' % field.field_type)(field)
self.fields[field_name] = form_field
if isinstance(form_field, FormBuilderFileField):
self.file_fields.append(field_name)
def get_unique_field_name(self, field):
field_name = '%s' % (slugify(unidecode(field.label)).replace('-', '_'))
if field_name in self.field_names:
i = 1
while True:
if i > 1:
if i > 2:
field_name = field_name.rsplit('_', 1)[0]
field_name = '%s_%s' % (field_name, i)
if field_name not in self.field_names:
break
i += 1
self.field_names.append(field_name)
self.field_types[field_name] = field.field_type
return field_name
def split_choices(self, choices):
return [x.strip() for x in choices.split(',') if x.strip()]
def to_bool(self, value):
return value.lower() in ('yes', 'y', 'true', 't', '1')
def prepare_text(self, field):
kwargs = field.field_attrs()
if field.placeholder_text and not field.initial:
kwargs.update({
'widget': forms.TextInput({
'placeholder': field.placeholder_text
})
})
return forms.CharField(**kwargs)
def prepare_textarea(self, field):
kwargs = field.field_attrs()
kwargs.update({
'widget': forms.Textarea({
'placeholder': field.placeholder_text or '',
})
})
return forms.CharField(**kwargs)
def prepare_email(self, field):
kwargs = field.field_attrs()
kwargs.update({
'widget': forms.EmailInput({
'placeholder': field.placeholder_text or '',
'autocomplete': 'email',
}),
})
return forms.EmailField(**kwargs)
def prepare_checkbox(self, field):
kwargs = field.field_attrs()
if field.initial:
kwargs.update({
'initial': self.to_bool(field.initial)
})
return forms.BooleanField(**kwargs)
def prepare_checkbox_multiple(self, field):
kwargs = field.field_attrs()
kwargs.update({
'widget': forms.CheckboxSelectMultiple(),
'choices': field.get_choices(),
})
if field.initial:
kwargs.update({
'initial': self.split_choices(field.initial)
})
return forms.MultipleChoiceField(**kwargs)
def prepare_select(self, field):
kwargs = field.field_attrs()
if field.choice_values:
choice_list = field.get_choices()
if not field.required:
choice_list.insert(0, ('', field.placeholder_text or _('Please select an option')))
kwargs.update({
'choices': choice_list
})
return forms.ChoiceField(**kwargs)
def prepare_radio(self, field):
kwargs = field.field_attrs()
kwargs.update({
'widget': forms.RadioSelect(),
'choices': field.get_choices(),
})
return forms.ChoiceField(**kwargs)
def prepare_file(self, field):
kwargs = field.field_attrs()
if field.choice_values:
regex = re.compile('[\s]*\n[\s]*')
choices = regex.split(field.choice_values)
kwargs.update({
'allowed_file_types': [i.lstrip('.').lower() for i in choices]
})
return FormBuilderFileField(**kwargs)
def prepare_date(self, field):
kwargs = field.field_attrs()
kwargs.update({
'widget': DateInput(),
})
return forms.DateField(**kwargs)
def prepare_time(self, field):
# @todo: needs proper widget
kwargs = field.field_attrs()
kwargs.update({
'widget': TimeInput(),
})
return forms.TimeField(**kwargs)
def prepare_hidden(self, field):
kwargs = field.field_attrs()
kwargs.update({
'widget': forms.HiddenInput(),
})
return forms.CharField(**kwargs)
def prepare_number(self, field):
kwargs = field.field_attrs()
return forms.IntegerField(**kwargs)
def prepare_url(self, field):
kwargs = field.field_attrs()
return forms.URLField(**kwargs)
def prepare_password(self, field):
kwargs = field.field_attrs()
kwargs.update({
'widget': forms.PasswordInput(),
})
return forms.CharField(**kwargs)
def prepare_phone(self, field):
kwargs = field.field_attrs()
kwargs.update({
'widget': TelephoneInput(),
})
return forms.CharField(**kwargs)
def save(self, request):
form_data = []
for field in self.field_names:
value = self.cleaned_data[field]
if hasattr(value, 'url'):
value = value.url
form_data.append({
'name': field,
'label': self.fields[field].label,
'value': value,
'type': self.field_types[field],
})
referrer = self.cleaned_data.get('referrer', '')
if self.form_definition.save_data:
self.save_to_db(form_data, request=request, referrer=referrer)
if self.form_definition.email_to:
self.email_submission(form_data, request=request, referrer=referrer)
def save_to_db(self, form_data, request, referrer):
user = request.user if request.user.is_authenticated() else None
# Если есть сессия для этой формы, то сохраняем в неё.
form_slug = "form-%d" % (self.form_definition.plugin_reference.id or self.form_definition.id)
if request.session.get(form_slug, False):
try:
form_instance = FormSubmission.objects.get(id=request.session.get(form_slug, False))
# Удаляем сессию, если это последний шаг. Признак последнего шага
for data in form_data:
if data['name'] == 'clear_session':
request.session[form_slug] = None
form_instance = None
if form_instance:
form_instance.form_data += form_data
form_instance.save()
return
except (FormSubmission.DoesNotExist):
pass
form_instance = FormSubmission.objects.create(
plugin=self.form_definition.plugin_reference,
ip=get_ip(request),
referrer=referrer,
form_data=form_data,
created_by=user)
request.session[form_slug] = form_instance.id
def email_submission(self, form_data, request, referrer):
# Отправляем, только если конец сессии
# Если есть сессия для этой формы, то сохраняем в неё.
form_slug = "form-%d" % (self.form_definition.plugin_reference.id or self.form_definition.id)
if request.session.get(form_slug, False):
try:
form_instance = FormSubmission.objects.get(id=request.session.get(form_slug, False))
form_data = form_instance.form_data
except (FormSubmission.DoesNotExist):
form_instance = None
pass
send = False
contacts = []
client_mail_to = None
for data in form_data:
if data['name'] == 'end_session':
request.session[form_slug] = None
request.session['last-completed-form-id'] = form_instance.id
send = True
if data['type'] == 'email':
client_mail_to = data['value']
if client_mail_to:
contacts.append(client_mail_to)
if data['name'] == 'phone' and data['value']:
contacts.append(data['value'])
# Simple antispam
if data['name'] == 'comment' and (data['value'].find('http://') > -1 or data['value'].find('www.') > -1):
request.session[form_slug] = None
return
if data['name'] == 'phone' and data['value'] in ['4955331826', '123456']:
request.session[form_slug] = None
return
if data['name'] == 'message' and data['value']:
request.session[form_slug] = None
return
# Some contacts
if not send or len(contacts) == 0:
return
# Send to company
mail_to = re.compile('\s*[,;]+\s*').split(self.form_definition.email_to)
mail_from = self.form_definition.email_from or None
mail_subject = self.form_definition.email_subject or \
u'%s%d – заявка с сайта' % (self.form_definition.name, form_instance.id)
context = {
'form': self.form_definition,
'referrer': referrer,
'title': mail_subject,
'form_data': form_data,
'request': request,
'recipients': mail_to,
}
# TODO: Перевод на английский и китайский
message = render_to_string('djangocms_forms/email_template/email.txt', context)
message_html = render_to_string('djangocms_forms/email_template/email.html', context)
email = EmailMultiAlternatives(mail_subject, message, mail_from, mail_to)
email.attach_alternative(message_html, 'text/html')
if self.files and self.form_definition.email_uploaded_files:
for file_path in self.files:
email.attach_file(file_path)
email.send(fail_silently=False)
# Send to client
if client_mail_to:
mail_to = re.compile('\s*[,;]+\s*').split(client_mail_to)
mail_from = self.form_definition.email_from or None
mail_subject = self.form_definition.email_subject or \
u'%s. Обращение №%d' % (self.form_definition.name, form_instance.id)
context = {
'form': self.form_definition,
'referrer': referrer,
'title': mail_subject,
'form_data': form_data,
'request': request,
'recipients': mail_to,
'form_instance': form_instance
}
message = render_to_string('djangocms_forms/email_template/client_email.txt', context)
message_html = render_to_string('djangocms_forms/email_template/client_email.html', context)
email = EmailMultiAlternatives(mail_subject, message, mail_from, mail_to)
email.attach_alternative(message_html, 'text/html')
email.send(fail_silently=False)
class SubmissionExportForm(forms.Form):
FORMAT_CHOICES = (
('csv', _('CSV')),
('json', _('JSON')),
('yaml', _('YAML')),
('xls', _('Microsoft Excel')),
)
form = forms.ModelChoiceField(
queryset=Form.active_objects.all(), label=_('Select a Form'),
error_messages={'required': _('Please select a form.')},
help_text=_('Select the form you would like to export entry data from. '
'You may only export data from one form at a time.'))
file_type = forms.ChoiceField(choices=FORMAT_CHOICES, initial='xls', required=False)
headers = MultipleChoiceAutoCompleteField(
label=_('Fields'), required=False,
widget=FilteredSelectMultiple(verbose_name=_('Fields'), is_stacked=False),)
from_date = forms.DateField(
label=_('From date'), required=False, widget=AdminDateWidget)
to_date = forms.DateField(
label=_('To date'), required=False, widget=AdminDateWidget)