master
fefa4ka 11 years ago
parent 4ddec83217
commit b333bbf534
  1. 2
      app/deploy/tasks.py
  2. 2
      app/settings.py
  3. 3
      djangocms_forms/__init__.py
  4. 263
      djangocms_forms/admin.py
  5. 7
      djangocms_forms/apps.py
  6. 117
      djangocms_forms/cms_plugins.py
  7. 15
      djangocms_forms/compat.py
  8. 66
      djangocms_forms/conf.py
  9. 86
      djangocms_forms/fields.py
  10. 331
      djangocms_forms/forms.py
  11. 9
      djangocms_forms/managers.py
  12. 97
      djangocms_forms/migrations/0001_initial.py
  13. 26
      djangocms_forms/migrations/0002_alter_model_options.py
  14. 19
      djangocms_forms/migrations/0003_add_referrer_field.py
  15. 0
      djangocms_forms/migrations/__init__.py
  16. 176
      djangocms_forms/models.py
  17. 35
      djangocms_forms/settings.py
  18. 5
      djangocms_forms/signals.py
  19. 226
      djangocms_forms/south_migrations/0001_initial.py
  20. 168
      djangocms_forms/south_migrations/0002_auto__add_field_formsubmission_referrer.py
  21. 0
      djangocms_forms/south_migrations/__init__.py
  22. 43
      djangocms_forms/static/css/djangocms_forms/admin/djangocms_forms.css
  23. 53
      djangocms_forms/static/js/djangocms_forms/admin/jquery-form-export.js
  24. 66
      djangocms_forms/static/js/djangocms_forms/admin/jquery-inline-collapsible.js
  25. 95
      djangocms_forms/static/js/djangocms_forms/admin/jquery-inline-positioning.js
  26. 45
      djangocms_forms/static/js/djangocms_forms/admin/jquery-inline-rename.js
  27. 35
      djangocms_forms/static/js/djangocms_forms/admin/jquery-inline-toggle-fields.js
  28. 100
      djangocms_forms/static/js/djangocms_forms/djangocms_forms.js
  29. 13
      djangocms_forms/static/js/djangocms_forms/libs/jquery-ui.min.js
  30. 11
      djangocms_forms/static/js/djangocms_forms/libs/jquery.form.min.js
  31. 4
      djangocms_forms/static/js/djangocms_forms/libs/jquery.min.js
  32. 26
      djangocms_forms/templates/admin/djangocms_forms/formsubmission/change_form.html
  33. 15
      djangocms_forms/templates/admin/djangocms_forms/formsubmission/change_list.html
  34. 22
      djangocms_forms/templates/admin/djangocms_forms/formsubmission/export_form.html
  35. 19
      djangocms_forms/templates/djangocms_forms/email_template/email.html
  36. 11
      djangocms_forms/templates/djangocms_forms/email_template/email.txt
  37. 61
      djangocms_forms/templates/djangocms_forms/form_template/default.html
  38. 0
      djangocms_forms/templatetags/__init__.py
  39. 89
      djangocms_forms/templatetags/djangocms_forms_tags.py
  40. 62
      djangocms_forms/uploads.py
  41. 8
      djangocms_forms/urls.py
  42. 17
      djangocms_forms/utils.py
  43. 61
      djangocms_forms/views.py
  44. 17
      djangocms_forms/widgets.py
  45. 14
      zsite/static/less/service.less
  46. 4
      zsite/templates/base.html
  47. 0
      zsite/templates/form_template/wizard.html
  48. 14
      zsite/templates/menu_footer.html

@ -42,7 +42,7 @@ common_configure = [
{"action": "apt",
"params": ["git",
"python-setuptools", "python-dev", "build-essential", "python-pip", "libmysqlclient-dev",
"mysql-server", "mysql-client"],
"mysql-server", "mysql-client", "libjpeg-dev"],
"message":"Installing apt-get packages" },
# {"action": "sudo", "params": "mysql_secure_installation",

@ -218,7 +218,7 @@ INSTALLED_APPS = (
'aldryn_bootstrap3',
'djangocms_forms',
'djangocms_forms'
# 'debug_toolbar',
)

@ -0,0 +1,3 @@
__version__ = '0.1.6'
default_app_config = 'djangocms_forms.apps.DjangoCMSFormsConfig'

@ -0,0 +1,263 @@
import datetime
from functools import update_wrapper
from django.contrib import admin, messages
from django.contrib.admin.helpers import AdminErrorList, AdminForm
from django.contrib.auth import get_permission_codename
from django.contrib.auth.admin import csrf_protect_m
from django.core.exceptions import PermissionDenied
from django.http import HttpResponse
from django.shortcuts import redirect
from django.template.defaultfilters import slugify, yesno
from django.template.response import TemplateResponse
from django.utils import timezone
from django.utils.encoding import force_text
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from tablib import Dataset
from .conf import settings
from .forms import SubmissionExportForm
from .models import Form, FormSubmission
try:
from django.contrib.admin.utils import unquote
except ImportError:
from django.contrib.admin.util import unquote
try:
from django.http import JsonResponse
except ImportError:
from .compat import JsonResponse
class FormFilter(admin.SimpleListFilter):
title = _('Forms')
parameter_name = 'form'
def lookups(self, request, model_admin):
forms = Form.active_objects.all()
for obj in forms:
yield (
str(obj.id), u'%s (%s)' % (obj.name, obj.submission_count)
)
def queryset(self, request, queryset):
if self.value():
return queryset.filter(plugin_id=self.value())
class FormSubmissionAdmin(admin.ModelAdmin):
change_form_template = 'admin/djangocms_forms/formsubmission/change_form.html'
change_list_template = 'admin/djangocms_forms/formsubmission/change_list.html'
export_form_template = 'admin/djangocms_forms/formsubmission/export_form.html'
list_display = ('plugin', 'creation_date_display', 'created_by', 'ip', 'referrer', )
list_filter = (FormFilter, )
readonly_fields = ('creation_date_display', 'created_by', 'plugin', 'ip', 'referrer', )
date_hierarchy = 'creation_date'
fieldsets = (
(None, {
'fields': ('creation_date_display', 'created_by', 'ip', 'referrer', )
}),
)
class Media:
js = (
'js/djangocms_forms/admin/jquery-form-export.js',
)
def has_add_permission(self, request):
return False
def has_export_permission(self, request):
opts = self.opts
codename = get_permission_codename('export', opts)
return request.user.has_perm('%s.%s' % (opts.app_label, codename))
def creation_date_display(self, obj):
return obj.creation_date.strftime(settings.DJANGOCMS_FORMS_DATETIME_FORMAT)
creation_date_display.short_description = _('Sent On')
def get_queryset(self, request):
qs = super(FormSubmissionAdmin, self).get_queryset(request)
return qs.select_related('created_by', 'plugin', )
def get_urls(self):
"""
Add the export view to urls.
"""
urls = super(FormSubmissionAdmin, self).get_urls()
from django.conf.urls import patterns, url
def wrap(view):
def wrapper(*args, **kwargs):
return self.admin_site.admin_view(view)(*args, **kwargs)
return update_wrapper(wrapper, view)
info = self.model._meta.app_label, self.model._meta.model_name
extra_urls = patterns(
'',
url(r'^export/$', wrap(self.export_view), name='%s_%s_export' % info),
)
return extra_urls + urls
@csrf_protect_m
def changelist_view(self, request, extra_context=None):
context = extra_context or {}
context.update({
'title': self.opts.verbose_name_plural.title(),
'has_export_permission': self.has_export_permission(request),
})
return super(FormSubmissionAdmin, self).changelist_view(
request, extra_context=context)
def change_view(self, request, object_id, form_url='', extra_context=None):
context = extra_context or {}
obj = self.get_object(request, unquote(object_id))
if obj:
context.update({
'title': force_text(obj.plugin),
})
return super(FormSubmissionAdmin, self).change_view(
request, object_id, form_url=form_url,
extra_context=context)
def export_view(self, request, form_url=''):
"""The 'export' admin view for this model."""
info = self.opts.app_label, self.opts.model_name
if not self.has_export_permission(request):
raise PermissionDenied
form = SubmissionExportForm(data=request.POST if request.method == 'POST' else None)
if form.is_valid():
data = form.cleaned_data
queryset = self.get_queryset(request) \
.filter(plugin_id=data.get('form')) \
.select_related('created_by', 'plugin', )
from_date, to_date = data.get('from_date'), data.get('to_date')
headers = data.get('headers', [])
if from_date:
queryset = queryset.filter(creation_date__gte=from_date)
if to_date:
queryset = queryset.filter(creation_date__lt=to_date + datetime.timedelta(days=1))
if not queryset.exists():
message = _('No matching %s found for the given criteria. '
'Please try again.') % self.opts.verbose_name_plural
self.message_user(request, message, level=messages.WARNING)
if request.is_ajax():
data = {
'reloadBrowser': True,
'submissionCount': 0,
}
return JsonResponse(data)
return redirect('admin:%s_%s_export' % info)
latest_submission = queryset[:1].get()
dataset = Dataset(title=latest_submission.plugin.name)
if not headers:
headers = [field['label'].strip() for field in latest_submission.form_data]
for submission in queryset:
for field in submission.form_data:
label = field['label'].strip()
if label not in headers:
headers.append(label)
if request.is_ajax():
data = {
'reloadBrowser': False,
'submissionCount': queryset.count(),
'availableHeaders': headers,
}
return JsonResponse(data)
headers.extend(['Submitted on', 'Sender IP', 'Referrer URL'])
dataset.headers = headers
def humanize(field):
value = field['value']
field_type = field['type']
if value in (None, '', [], (), {}):
return None
if field_type == 'checkbox':
value = yesno(bool(value), u'{0},{1}'.format(_('Yes'), _('No')))
if field_type == 'checkbox_multiple':
value = ', '.join(list(value))
return value
for submission in queryset:
row = [None] * len(headers)
for field in submission.form_data:
label = field['label'].strip()
if label in headers:
row[headers.index(label)] = humanize(field)
row[-3] = submission.creation_date.strftime(
settings.DJANGOCMS_FORMS_DATETIME_FORMAT)
row[-2] = submission.ip
row[-1] = submission.referrer
dataset.append(row)
mimetype = {
'xls': 'application/vnd.ms-excel',
'csv': 'text/csv',
'html': 'text/html',
'yaml': 'text/yaml',
'json': 'application/json',
}
file_type = data.get('file_type', 'xls')
filename = settings.DJANGOCMS_FORMS_EXPORT_FILENAME.format(
form_name=slugify(latest_submission.plugin.name))
filename = timezone.now().strftime(filename)
filename = '%s.%s' % (filename, file_type)
response = HttpResponse(
getattr(dataset, file_type), {
'content_type': mimetype.get(file_type, 'application/octet-stream')
})
response['Content-Disposition'] = 'attachment; filename=%s' % filename
return response
# Wrap in all admin layout
fieldsets = ((None, {'fields': form.fields.keys()}),)
adminform = AdminForm(form, fieldsets, {}, model_admin=self)
media = self.media + adminform.media
context = {
'title': _('Export %s') % force_text(self.opts.verbose_name_plural),
'adminform': adminform,
'is_popup': '_popup' in request.REQUEST,
'media': mark_safe(media),
'errors': AdminErrorList(form, ()),
'app_label': self.opts.app_label,
}
return self.render_export_form(request, context, form_url)
def render_export_form(self, request, context, form_url=''):
"""
Render the from submission export form.
"""
context.update({
'has_change_permission': self.has_change_permission(request),
'form_url': mark_safe(form_url),
'opts': self.opts,
'add': True,
'save_on_top': self.save_on_top,
})
return TemplateResponse(request, self.export_form_template, context)
admin.site.register(FormSubmission, FormSubmissionAdmin)

@ -0,0 +1,7 @@
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
class DjangoCMSFormsConfig(AppConfig):
name = 'djangocms_forms'
verbose_name = _('Forms')

@ -0,0 +1,117 @@
from cms.plugin_base import CMSPluginBase
from cms.plugin_pool import plugin_pool
from django import forms
from django.contrib import admin
from django.db import models
from django.template.loader import select_template
from django.utils.translation import ugettext_lazy as _
from .conf import settings
from .forms import FormBuilder, FormDefinitionAdminForm, FormFieldInlineForm
from .models import FormDefinition, FormField
class FormFieldInline(admin.StackedInline):
model = FormField
form = FormFieldInlineForm
extra = 0
fieldsets = (
(None, {
'fields': (('label', 'field_type', 'required'),
'initial', 'placeholder_text', 'help_text',
'choice_values', 'position', )
}),
)
formfield_overrides = {
models.TextField: {
'widget': forms.Textarea(
attrs={'rows': 4, 'cols': 50})
},
}
class Media:
css = {
'all': ('css/djangocms_forms/admin/djangocms_forms.css',)
}
js = (
'js/djangocms_forms/libs/jquery.min.js',
'js/djangocms_forms/libs/jquery-ui.min.js',
'js/djangocms_forms/admin/jquery-inline-positioning.js',
'js/djangocms_forms/admin/jquery-inline-rename.js',
'js/djangocms_forms/admin/jquery-inline-collapsible.js',
'js/djangocms_forms/admin/jquery-inline-toggle-fields.js',
)
class FormPlugin(CMSPluginBase):
name = settings.DJANGOCMS_FORMS_PLUGIN_NAME
module = settings.DJANGOCMS_FORMS_PLUGIN_MODULE
model = FormDefinition
cache = False
form = FormDefinitionAdminForm
inlines = (FormFieldInline, )
render_template = settings.DJANGOCMS_FORMS_DEFAULT_TEMPLATE
def get_fieldsets(self, request, obj=None):
if settings.DJANGOCMS_FORMS_FIELDSETS:
return settings.DJANGOCMS_FORMS_FIELDSETS
fieldsets = (
(None, {'fields': ('name', )}),
(None, {
'description': _('The <strong>Title</strong> and <strong>Description</strong> '
'will display above the input fields and Submit button.'),
'fields': ('title', 'description', )
}),
(None, {
'description': _('By default, the Submit Button will say <strong>Submit</strong>. '
'You can change this to say whatever you want'),
'fields': ('submit_btn_txt', 'form_template', )
}),
(None, {
'description': _('You can also change the message that appears after someone '
'submits your form. '
'By default, this says <strong>Thank you!</strong>, '
'but you are welcome to change this text as well.'),
'fields': ('post_submit_msg', )
}),
(None, {
'fields': ('success_redirect', ('page_redirect', 'external_redirect'), ),
}),
(None, {
'description': '<strong>Submission Settings</strong> &mdash; '
'Choose storage options to capture form data. You can enter '
'an email address to have the form submissions emailed to you or '
'log all the form submissions to the database.',
'fields': ('email_to', 'email_from', 'email_subject',
'email_uploaded_files', 'save_data', ),
}),
)
return fieldsets
def get_render_template(self, context, instance, placeholder):
# returns the first template that exists, falling back to bundled template
return select_template([
instance.form_template,
settings.DJANGOCMS_FORMS_DEFAULT_TEMPLATE,
'djangocms_forms/form_template/default.html'
])
def render(self, context, instance, placeholder):
context = super(FormPlugin, self).render(context, instance, placeholder)
request = context['request']
form = FormBuilder(
initial={'referrer': request.path_info}, form_definition=instance,
label_suffix='', auto_id='%s')
context.update({
'form': form
})
return context
plugin_pool.register_plugin(FormPlugin)

@ -0,0 +1,15 @@
import json
from django.core.serializers.json import DjangoJSONEncoder
from django.http import HttpResponse
class JsonResponse(HttpResponse):
def __init__(self, data, encoder=DjangoJSONEncoder, safe=True, **kwargs):
if safe and not isinstance(data, dict):
raise TypeError('In order to allow non-dict objects to be '
'serialized set the safe parameter to False')
kwargs.setdefault('content_type', 'application/json')
data = json.dumps(data, cls=encoder)
super(JsonResponse, self).__init__(content=data, **kwargs)

@ -0,0 +1,66 @@
# -*- coding: utf-8 -*-
from appconf import AppConf
from django.conf import settings # noqa
from django.utils.translation import ugettext_lazy as _
class DjangoCMSFormsConf(AppConf):
PLUGIN_MODULE = _('Generic')
PLUGIN_NAME = _('Form')
FIELDSETS = None
FILE_STORAGE_DIR = 'djangocms_forms'
FILE_STORAGE = settings.DEFAULT_FILE_STORAGE
ALLOWED_FILE_TYPES = (
'aac', 'ace', 'ai', 'aiff', 'avi', 'bmp', 'dir', 'doc', 'docx', 'dmg',
'eps', 'fla', 'flv', 'gif', 'gz', 'hqx', 'ico', 'indd', 'inx', 'jpg',
'jar', 'jpeg', 'md', 'mov', 'mp3', 'mp4', 'mpc', 'mkv', 'mpg', 'mpeg',
'ogg', 'odg', 'odf', 'odp', 'ods', 'odt', 'otf', 'pdf', 'png', 'pps',
'ppsx', 'ps', 'psd', 'rar', 'rm', 'rtf', 'sit', 'swf', 'tar', 'tga',
'tif', 'tiff', 'ttf', 'txt', 'wav', 'wma', 'wmv', 'xls', 'xlsx', 'xml',
'zip'
)
MAX_UPLOAD_SIZE = 5242880 # 5MB
FIELD_TYPES = (
('text', _('Text')),
('textarea', _('Text Area')),
('email', _('Email')),
('number', _('Number')),
('phone', _('Phone')),
('url', _('URL')),
('checkbox', _('Checkbox')),
('checkbox_multiple', _('Multi Checkbox')),
('select', _('Drop down')),
('radio', _('Radio')),
('file', _('File Upload')),
('date', _('Date')),
('time', _('Time')),
('password', _('Password')),
('hidden', _('Hidden')),
)
DEFAULT_FIELD_TYPE = 'text'
SPAM_PROTECTIONS = (
(0, _('None')),
(1, _('Honeypot')),
(2, _('ReCAPTCHA')),
)
DEFAULT_SPAM_PROTECTION = 0
TEMPLATES = (
('djangocms_forms/form_template/default.html', _('Default')),
)
DEFAULT_TEMPLATE = 'djangocms_forms/form_template/default.html'
DATETIME_FORMAT = '%d/%m/%Y %H:%M'
EXPORT_FILENAME = 'export-{form_name}-%Y-%m-%d'
HASHIDS_SALT = settings.SECRET_KEY
class Meta:
prefix = 'djangocms_forms'

@ -0,0 +1,86 @@
import os
from django import forms
from django.core.exceptions import ValidationError
from django.db import models
from django.db.models import SET_NULL
from django.template.defaultfilters import filesizeformat
from django.utils.translation import ugettext_lazy as _
from .conf import settings
class FormBuilderFileField(forms.FileField):
def __init__(self, *args, **kwargs):
self.max_upload_size = kwargs.pop(
'max_upload_size', settings.DJANGOCMS_FORMS_MAX_UPLOAD_SIZE)
allowed_file_types = kwargs.pop(
'allowed_file_types', settings.DJANGOCMS_FORMS_ALLOWED_FILE_TYPES)
self.allowed_file_types = [i.lstrip('.').lower()
for i in allowed_file_types]
super(FormBuilderFileField, self).__init__(*args, **kwargs)
def clean(self, *args, **kwargs):
uploaded_file = super(FormBuilderFileField, self).clean(*args, **kwargs)
if not uploaded_file:
if self.required:
raise forms.ValidationError(_('This field is required.'))
return uploaded_file
if not os.path.splitext(uploaded_file.name)[1].lstrip('.').lower() in \
self.allowed_file_types:
raise forms.ValidationError(
_('Sorry, this filetype is not allowed. '
'Allowed filetype: %s') % ', '.join(self.allowed_file_types))
if uploaded_file._size > self.max_upload_size:
params = {
'max_size': filesizeformat(self.max_upload_size),
'size': filesizeformat(uploaded_file._size)
}
msg = _(
'Please keep file size under %(max_size)s. Current size is %(size)s.') % params
raise forms.ValidationError(msg)
return uploaded_file
class PluginReferenceField(models.ForeignKey):
def __init__(self, *args, **kwargs):
kwargs.update({'null': True}) # always allow Null
kwargs.update({'editable': False}) # never allow edits in admin
kwargs.update({'on_delete': SET_NULL}) # never delete plugin
super(PluginReferenceField, self).__init__(*args, **kwargs)
def _create(self, model_instance):
return self.rel.to._default_manager.create(name=model_instance.name)
def pre_save(self, model_instance, add):
if not model_instance.pk and add:
setattr(model_instance, self.name, self._create(model_instance))
else:
reference = getattr(model_instance, self.name)
if not reference:
setattr(model_instance, self.name, self._create(model_instance))
reference = getattr(model_instance, self.name)
if reference.name != model_instance.name:
reference.name = model_instance.name
reference.save()
return super(PluginReferenceField, self).pre_save(model_instance, add)
def south_field_triple(self):
"""Returns a suitable description of this field for South."""
# We'll just introspect ourselves, since we inherit.
from south.modelsinspector import introspector
field_class = 'django.db.models.fields.related.ForeignKey'
args, kwargs = introspector(self)
return (field_class, args, kwargs)
class MultipleChoiceAutoCompleteField(forms.MultipleChoiceField):
def validate(self, value):
if self.required and not value:
raise ValidationError(self.error_messages['required'], code='required')
return value

@ -0,0 +1,331 @@
#!/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
if request.session.get(form_slug, False):
try:
form_instance = FormSubmission.objects.get(id=request.session.get(form_slug, False))
form_instance.form_data += form_data
form_instance.save()
# Удаляем сессию, если это последний шаг. Признак последнего шага
for data in form_data:
if data['name'] == 'end_session':
request.session[form_slug] = None
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):
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 \
'Form Submission - %s' % self.form_definition.name
context = {
'form': self.form_definition,
'referrer': referrer,
'title': mail_subject,
'form_data': form_data,
'request': request,
'recipients': mail_to,
}
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)
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)

@ -0,0 +1,9 @@
from django.db import models
from django.db.models import Count
class ActiveFormManager(models.Manager):
def get_queryset(self):
qs = super(ActiveFormManager, self).get_queryset()
return qs.annotate(submission_count=Count('submissions')) \
.filter(submission_count__gt=0)

@ -0,0 +1,97 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import jsonfield.fields
import djangocms_forms.fields
import django.db.models.deletion
import cms.models.fields
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
('cms', '__first__'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Form',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('name', models.CharField(verbose_name='Name', max_length=255, editable=False, db_index=True)),
],
options={
'verbose_name': 'Form',
'verbose_name_plural': 'Forms',
},
bases=(models.Model,),
),
migrations.CreateModel(
name='FormDefinition',
fields=[
('cmsplugin_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='cms.CMSPlugin')),
('name', models.CharField(max_length=255, verbose_name='Form Name')),
('title', models.CharField(max_length=150, verbose_name='Title', blank=True)),
('description', models.TextField(verbose_name='Description', blank=True)),
('submit_btn_txt', models.CharField(default='Submit', help_text="Text for the Submit Button. The default is 'Submit'", max_length=100, verbose_name='Submit Button Text')),
('post_submit_msg', models.TextField(default='Thank You', help_text='Display this message to users after they submit your form.', verbose_name='Post Submit Message', blank=True)),
('success_redirect', models.BooleanField(default=False, help_text='HTTP redirect after successful submission', verbose_name='Redirect?')),
('external_redirect', models.URLField(help_text='e.g. http://example.com/thank-you', verbose_name='External URL', blank=True)),
('email_to', models.CharField(help_text='Separate several addresses with a comma.', max_length=255, verbose_name='Send form data to e-mail address', blank=True)),
('email_from', models.EmailField(max_length=255, verbose_name='Sender Email Address', blank=True)),
('email_subject', models.CharField(max_length=255, verbose_name='Email Subject', blank=True)),
('email_uploaded_files', models.BooleanField(default=True, verbose_name='Send uploaded files as email attachments')),
('save_data', models.BooleanField(default=True, help_text='Logs all form submissions to the database.', verbose_name='Save to database')),
('spam_protection', models.SmallIntegerField(default=0, verbose_name='Spam Protection', choices=[(0, 'None'), (1, 'Honeypot'), (2, 'ReCAPTCHA')])),
('form_template', models.CharField(default=b'djangocms_forms/form_template/default.html', max_length=150, verbose_name='Form Template', blank=True, choices=[(b'djangocms_forms/form_template/default.html', 'Default')])),
('page_redirect', cms.models.fields.PageField(on_delete=django.db.models.deletion.SET_NULL, blank=True, to='cms.Page', help_text='A page has priority over an external URL', null=True, verbose_name='Page URL')),
('plugin_reference', djangocms_forms.fields.PluginReferenceField(related_name='plugin', on_delete=django.db.models.deletion.SET_NULL, editable=False, to='djangocms_forms.Form', null=True)),
],
options={
'verbose_name': 'Form',
'verbose_name_plural': 'Forms',
},
bases=('cms.cmsplugin',),
),
migrations.CreateModel(
name='FormField',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('field_type', models.CharField(default=b'text', max_length=100, verbose_name='Field Type', choices=[(b'text', 'Text'), (b'textarea', 'Text Area'), (b'email', 'Email'), (b'number', 'Number'), (b'phone', 'Phone'), (b'url', 'URL'), (b'checkbox', 'Checkbox'), (b'checkbox_multiple', 'Multi Checkbox'), (b'select', 'Drop down'), (b'radio', 'Radio'), (b'file', 'File Upload'), (b'date', 'Date'), (b'time', 'Time'), (b'password', 'Password'), (b'hidden', 'Hidden')])),
('label', models.CharField(max_length=255, verbose_name='name')),
('placeholder_text', models.CharField(max_length=100, verbose_name='Placeholder Text', blank=True)),
('required', models.BooleanField(default=True, verbose_name='Required')),
('help_text', models.TextField(help_text='A description / instructions for this field.', verbose_name='Description', blank=True)),
('initial', models.CharField(max_length=255, verbose_name='Default Value', blank=True)),
('choice_values', models.TextField(help_text='Enter options one per line. For "File Upload" field type, enter allowed filetype (e.g .pdf) one per line.', verbose_name='Choices', blank=True)),
('position', models.PositiveIntegerField(null=True, verbose_name='Position', blank=True)),
('form', models.ForeignKey(related_name='fields', to='djangocms_forms.FormDefinition')),
],
options={
'ordering': ('position',),
'verbose_name': 'field',
'verbose_name_plural': 'fields',
},
bases=(models.Model,),
),
migrations.CreateModel(
name='FormSubmission',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('creation_date', models.DateTimeField(auto_now=True, verbose_name='Date')),
('ip', models.GenericIPAddressField(null=True, verbose_name=b'IP', blank=True)),
('form_data', jsonfield.fields.JSONField(verbose_name='Form Data')),
('created_by', models.ForeignKey(editable=False, to=settings.AUTH_USER_MODEL, null=True, verbose_name='User')),
('plugin', models.ForeignKey(related_name='submissions', editable=False, to='djangocms_forms.Form', verbose_name='Form')),
],
options={
'ordering': ('-creation_date',),
'verbose_name': 'Form Submission',
'verbose_name_plural': 'Form Submissions',
},
bases=(models.Model,),
),
]

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('djangocms_forms', '0001_initial'),
]
operations = [
migrations.AlterModelOptions(
name='form',
options={'verbose_name': 'form', 'verbose_name_plural': 'forms'},
),
migrations.AlterModelOptions(
name='formdefinition',
options={'verbose_name': 'form', 'verbose_name_plural': 'forms'},
),
migrations.AlterModelOptions(
name='formsubmission',
options={'ordering': ('-creation_date',), 'verbose_name': 'form submission', 'verbose_name_plural': 'form submissions', 'permissions': (('export_formsubmission', 'Can export Form Submission'),)},
),
]

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('djangocms_forms', '0002_alter_model_options'),
]
operations = [
migrations.AddField(
model_name='formsubmission',
name='referrer',
field=models.CharField(max_length=150, verbose_name='Referrer URL', blank=True),
),
]

@ -0,0 +1,176 @@
import re
from cms.models import CMSPlugin
from cms.models.fields import PageField
from django.db import models
from django.template.defaultfilters import slugify
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _
from jsonfield import JSONField
from unidecode import unidecode
from .conf import settings
from .fields import PluginReferenceField
from .managers import ActiveFormManager
@python_2_unicode_compatible
class Form(models.Model):
name = models.CharField(_('Name'), max_length=255, db_index=True, editable=False)
objects = models.Manager()
active_objects = ActiveFormManager()
class Meta:
verbose_name = _('form')
verbose_name_plural = _('forms')
def __str__(self):
return self.name
@python_2_unicode_compatible
class FormDefinition(CMSPlugin):
name = models.CharField(_('Form Name'), max_length=255)
title = models.CharField(_('Title'), max_length=150, blank=True)
description = models.TextField(_('Description'), blank=True)
submit_btn_txt = models.CharField(
_('Submit Button Text'), max_length=100, default=_('Submit'),
help_text=_('Text for the Submit Button. The default is \'Submit\''))
post_submit_msg = models.TextField(
_('Post Submit Message'), blank=True, default=_('Thank You'),
help_text=_('Display this message to users after they submit your form.'))
# 'HTTP redirect after successful submission'
success_redirect = models.BooleanField(
_('Redirect?'), default=False,
help_text=_('HTTP redirect after successful submission'))
page_redirect = PageField(
verbose_name=_('Page URL'), blank=True, null=True,
on_delete=models.SET_NULL,
help_text=_('A page has priority over an external URL'))
external_redirect = models.URLField(
_('External URL'), blank=True,
help_text=_('e.g. http://example.com/thank-you'))
# Email
email_to = models.CharField(
_('Send form data to e-mail address'), max_length=255, blank=True,
help_text=_('Separate several addresses with a comma.'))
email_from = models.EmailField(_('Sender Email Address'), max_length=255, blank=True)
email_subject = models.CharField(_('Email Subject'), max_length=255, blank=True)
email_uploaded_files = models.BooleanField(
_('Send uploaded files as email attachments'), default=True)
# Save to database
save_data = models.BooleanField(
_('Save to database'), default=True,
help_text=_('Logs all form submissions to the database.'))
spam_protection = models.SmallIntegerField(
_('Spam Protection'),
choices=settings.DJANGOCMS_FORMS_SPAM_PROTECTIONS,
default=settings.DJANGOCMS_FORMS_DEFAULT_SPAM_PROTECTION)
form_template = models.CharField(
_('Form Template'), max_length=150, blank=True,
choices=settings.DJANGOCMS_FORMS_TEMPLATES,
default=settings.DJANGOCMS_FORMS_DEFAULT_TEMPLATE,
)
plugin_reference = PluginReferenceField(Form, related_name='plugin')
class Meta:
verbose_name_plural = _('forms')
verbose_name = _('form')
def __str__(self):
return self.name
@property
def redirect_url(self):
if self.page_redirect:
return self.page_redirect.get_absolute_url()
elif self.external_redirect:
return self.external_redirect
@property
def upload_to(self):
return '%s-%s' % (
slugify(unidecode(self.name)).replace('_', '-'),
self.plugin_reference_id)
def copy_relations(self, oldinstance):
for field in oldinstance.fields.all():
field.pk = None
field.form = self
field.save()
@python_2_unicode_compatible
class FormField(models.Model):
form = models.ForeignKey(FormDefinition, related_name='fields')
field_type = models.CharField(
_('Field Type'), max_length=100,
choices=settings.DJANGOCMS_FORMS_FIELD_TYPES,
default=settings.DJANGOCMS_FORMS_DEFAULT_FIELD_TYPE)
label = models.CharField(_('name'), max_length=255)
placeholder_text = models.CharField(_('Placeholder Text'), blank=True, max_length=100)
required = models.BooleanField(_('Required'), default=True)
help_text = models.TextField(
_('Description'), blank=True,
help_text=_('A description / instructions for this field.'))
initial = models.CharField(_('Default Value'), max_length=255, blank=True)
choice_values = models.TextField(
_('Choices'), blank=True,
help_text=_('Enter options one per line. For "File Upload" '
'field type, enter allowed filetype (e.g .pdf) one per line.'))
position = models.PositiveIntegerField(_('Position'), blank=True, null=True)
class Meta:
verbose_name_plural = _('fields')
verbose_name = _('field')
ordering = ('position', )
def __str__(self):
return self.label
def field_attrs(self):
args = {
'required': self.required,
'label': self.label if self.label else '',
'initial': self.initial if self.initial else None,
'help_text': self.help_text,
}
return args
def get_choices(self):
if self.choice_values:
regex = re.compile('[\s]*\n[\s]*')
choices = regex.split(self.choice_values)
return [(str(choice.encode('utf-8')), str(choice.encode('utf-8'))) for choice in choices]
@python_2_unicode_compatible
class FormSubmission(models.Model):
plugin = models.ForeignKey(
Form, verbose_name=_('Form'), editable=False, related_name='submissions')
creation_date = models.DateTimeField(_('Date'), auto_now=True)
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL, verbose_name=_('User'), editable=False, null=True)
ip = models.GenericIPAddressField(verbose_name='IP', blank=True, null=True)
referrer = models.CharField(_('Referrer URL'), max_length=150, blank=True)
form_data = JSONField(_('Form Data'))
class Meta:
verbose_name_plural = _('form submissions')
verbose_name = _('form submission')
ordering = ('-creation_date', )
permissions = (
('export_formsubmission', 'Can export Form Submission'),
)
def __str__(self):
return u'%s' % self.plugin

@ -0,0 +1,35 @@
from django.utils.translation import ugettext_lazy as _
TEXT = 'text'
TEXTAREA = 'textarea'
EMAIL = 'email'
CHECKBOX = 'checkbox'
CHECKBOX_MULTIPLE = 'checkbox_multiple'
SELECT = 'select'
RADIO = 'radio'
FILE = 'file'
DATE = 'date'
TIME = 'time'
HIDDEN = 'hidden'
NUMBER = 'number'
URL = 'url'
PASSWORD = 'password'
PHONE = 'phone'
FIELD_TYPES = (
(TEXT, _('Text')),
(TEXTAREA, _('Text Area')),
(EMAIL, _('Email')),
(NUMBER, _('Number')),
(PHONE, _('Phone')),
(URL, _('URL')),
(CHECKBOX, _('Checkbox')),
(CHECKBOX_MULTIPLE, _('Multi Checkbox')),
(SELECT, _('Drop down')),
(RADIO, _('Radio')),
(FILE, _('File Upload')),
(DATE, _('Date')),
(TIME, _('Time')),
(PASSWORD, _('Password')),
(HIDDEN, _('Hidden')),
)

@ -0,0 +1,5 @@
from __future__ import unicode_literals
from django.dispatch import Signal
form_submission = Signal(providing_args=['form', 'cleaned_data'])

@ -0,0 +1,226 @@
# -*- coding: utf-8 -*-
from south.utils import datetime_utils as datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'Form'
db.create_table(u'djangocms_forms_form', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('name', self.gf('django.db.models.fields.CharField')(max_length=255, db_index=True)),
))
db.send_create_signal(u'djangocms_forms', ['Form'])
# Adding model 'FormDefinition'
db.create_table(u'djangocms_forms_formdefinition', (
(u'cmsplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['cms.CMSPlugin'], unique=True, primary_key=True)),
('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
('title', self.gf('django.db.models.fields.CharField')(max_length=150, blank=True)),
('description', self.gf('django.db.models.fields.TextField')(blank=True)),
('submit_btn_txt', self.gf('django.db.models.fields.CharField')(default=u'Submit', max_length=100)),
('post_submit_msg', self.gf('django.db.models.fields.TextField')(default=u'Thank You', blank=True)),
('success_redirect', self.gf('django.db.models.fields.BooleanField')(default=False)),
('page_redirect', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['cms.Page'], null=True, on_delete=models.SET_NULL, blank=True)),
('external_redirect', self.gf('django.db.models.fields.URLField')(max_length=200, blank=True)),
('email_to', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
('email_from', self.gf('django.db.models.fields.EmailField')(max_length=255, blank=True)),
('email_subject', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
('email_uploaded_files', self.gf('django.db.models.fields.BooleanField')(default=True)),
('save_data', self.gf('django.db.models.fields.BooleanField')(default=True)),
('spam_protection', self.gf('django.db.models.fields.SmallIntegerField')(default=0)),
('form_template', self.gf('django.db.models.fields.CharField')(default='djangocms_forms/form_template/default.html', max_length=150, blank=True)),
('plugin_reference', self.gf('django.db.models.fields.related.ForeignKey')(related_name='plugin', null=True, on_delete=models.SET_NULL, to=orm['djangocms_forms.Form'])),
))
db.send_create_signal(u'djangocms_forms', ['FormDefinition'])
# Adding model 'FormField'
db.create_table(u'djangocms_forms_formfield', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('form', self.gf('django.db.models.fields.related.ForeignKey')(related_name='fields', to=orm['djangocms_forms.FormDefinition'])),
('field_type', self.gf('django.db.models.fields.CharField')(default='text', max_length=100)),
('label', self.gf('django.db.models.fields.CharField')(max_length=255)),
('placeholder_text', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True)),
('required', self.gf('django.db.models.fields.BooleanField')(default=True)),
('help_text', self.gf('django.db.models.fields.TextField')(blank=True)),
('initial', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
('choice_values', self.gf('django.db.models.fields.TextField')(blank=True)),
('position', self.gf('django.db.models.fields.PositiveIntegerField')(null=True, blank=True)),
))
db.send_create_signal(u'djangocms_forms', ['FormField'])
# Adding model 'FormSubmission'
db.create_table(u'djangocms_forms_formsubmission', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('plugin', self.gf('django.db.models.fields.related.ForeignKey')(related_name='submissions', to=orm['djangocms_forms.Form'])),
('creation_date', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)),
('created_by', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True)),
('ip', self.gf('django.db.models.fields.GenericIPAddressField')(max_length=39, null=True, blank=True)),
('form_data', self.gf('jsonfield.fields.JSONField')()),
))
db.send_create_signal(u'djangocms_forms', ['FormSubmission'])
def backwards(self, orm):
# Deleting model 'Form'
db.delete_table(u'djangocms_forms_form')
# Deleting model 'FormDefinition'
db.delete_table(u'djangocms_forms_formdefinition')
# Deleting model 'FormField'
db.delete_table(u'djangocms_forms_formfield')
# Deleting model 'FormSubmission'
db.delete_table(u'djangocms_forms_formsubmission')
models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'cms.cmsplugin': {
'Meta': {'object_name': 'CMSPlugin'},
'changed_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'language': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}),
'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.CMSPlugin']", 'null': 'True', 'blank': 'True'}),
'placeholder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}),
'plugin_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
},
'cms.page': {
'Meta': {'ordering': "('tree_id', 'lft')", 'unique_together': "(('publisher_is_draft', 'application_namespace'), ('reverse_id', 'site', 'publisher_is_draft'))", 'object_name': 'Page'},
'application_namespace': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
'application_urls': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '200', 'null': 'True', 'blank': 'True'}),
'changed_by': ('django.db.models.fields.CharField', [], {'max_length': '70'}),
'changed_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.CharField', [], {'max_length': '70'}),
'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'in_navigation': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
'is_home': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
'languages': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
'limit_visibility_in_menu': ('django.db.models.fields.SmallIntegerField', [], {'default': 'None', 'null': 'True', 'db_index': 'True', 'blank': 'True'}),
'login_required': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'navigation_extenders': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '80', 'null': 'True', 'blank': 'True'}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['cms.Page']"}),
'placeholders': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['cms.Placeholder']", 'symmetrical': 'False'}),
'publication_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'publication_end_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'publisher_is_draft': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
'publisher_public': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'publisher_draft'", 'unique': 'True', 'null': 'True', 'to': "orm['cms.Page']"}),
'reverse_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '40', 'null': 'True', 'blank': 'True'}),
'revision_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
'site': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'djangocms_pages'", 'to': u"orm['sites.Site']"}),
'soft_root': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
'template': ('django.db.models.fields.CharField', [], {'default': "'INHERIT'", 'max_length': '100'}),
'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
'xframe_options': ('django.db.models.fields.IntegerField', [], {'default': '0'})
},
'cms.placeholder': {
'Meta': {'object_name': 'Placeholder'},
'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'slot': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
},
u'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
u'djangocms_forms.form': {
'Meta': {'object_name': 'Form'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
},
u'djangocms_forms.formdefinition': {
'Meta': {'object_name': 'FormDefinition', '_ormbases': ['cms.CMSPlugin']},
u'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'email_from': ('django.db.models.fields.EmailField', [], {'max_length': '255', 'blank': 'True'}),
'email_subject': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'email_to': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'email_uploaded_files': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'external_redirect': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
'form_template': ('django.db.models.fields.CharField', [], {'default': "'djangocms_forms/form_template/default.html'", 'max_length': '150', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'page_redirect': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Page']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
'plugin_reference': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'plugin'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['djangocms_forms.Form']"}),
'post_submit_msg': ('django.db.models.fields.TextField', [], {'default': "u'Thank You'", 'blank': 'True'}),
'save_data': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'spam_protection': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
'submit_btn_txt': ('django.db.models.fields.CharField', [], {'default': "u'Submit'", 'max_length': '100'}),
'success_redirect': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '150', 'blank': 'True'})
},
u'djangocms_forms.formfield': {
'Meta': {'ordering': "('position',)", 'object_name': 'FormField'},
'choice_values': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'field_type': ('django.db.models.fields.CharField', [], {'default': "'text'", 'max_length': '100'}),
'form': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'fields'", 'to': u"orm['djangocms_forms.FormDefinition']"}),
'help_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'initial': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'placeholder_text': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
'position': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
'required': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'djangocms_forms.formsubmission': {
'Meta': {'ordering': "('-creation_date',)", 'object_name': 'FormSubmission'},
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True'}),
'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'form_data': ('jsonfield.fields.JSONField', [], {}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
'plugin': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'submissions'", 'to': u"orm['djangocms_forms.Form']"})
},
u'sites.site': {
'Meta': {'ordering': "(u'domain',)", 'object_name': 'Site', 'db_table': "u'django_site'"},
'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
}
}
complete_apps = ['djangocms_forms']

@ -0,0 +1,168 @@
# -*- coding: utf-8 -*-
from south.utils import datetime_utils as datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'FormSubmission.referrer'
db.add_column(u'djangocms_forms_formsubmission', 'referrer',
self.gf('django.db.models.fields.CharField')(default='', max_length=150, blank=True),
keep_default=False)
def backwards(self, orm):
# Deleting field 'FormSubmission.referrer'
db.delete_column(u'djangocms_forms_formsubmission', 'referrer')
models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'cms.cmsplugin': {
'Meta': {'object_name': 'CMSPlugin'},
'changed_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'language': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}),
'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.CMSPlugin']", 'null': 'True', 'blank': 'True'}),
'placeholder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}),
'plugin_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
},
'cms.page': {
'Meta': {'ordering': "('tree_id', 'lft')", 'unique_together': "(('publisher_is_draft', 'application_namespace'), ('reverse_id', 'site', 'publisher_is_draft'))", 'object_name': 'Page'},
'application_namespace': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
'application_urls': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '200', 'null': 'True', 'blank': 'True'}),
'changed_by': ('django.db.models.fields.CharField', [], {'max_length': '70'}),
'changed_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.CharField', [], {'max_length': '70'}),
'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'in_navigation': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
'is_home': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
'languages': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
'limit_visibility_in_menu': ('django.db.models.fields.SmallIntegerField', [], {'default': 'None', 'null': 'True', 'db_index': 'True', 'blank': 'True'}),
'login_required': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'navigation_extenders': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '80', 'null': 'True', 'blank': 'True'}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['cms.Page']"}),
'placeholders': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['cms.Placeholder']", 'symmetrical': 'False'}),
'publication_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'publication_end_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'publisher_is_draft': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
'publisher_public': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'publisher_draft'", 'unique': 'True', 'null': 'True', 'to': "orm['cms.Page']"}),
'reverse_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '40', 'null': 'True', 'blank': 'True'}),
'revision_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
'site': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'djangocms_pages'", 'to': u"orm['sites.Site']"}),
'soft_root': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
'template': ('django.db.models.fields.CharField', [], {'default': "'INHERIT'", 'max_length': '100'}),
'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
'xframe_options': ('django.db.models.fields.IntegerField', [], {'default': '0'})
},
'cms.placeholder': {
'Meta': {'object_name': 'Placeholder'},
'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'slot': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
},
u'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
u'djangocms_forms.form': {
'Meta': {'object_name': 'Form'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
},
u'djangocms_forms.formdefinition': {
'Meta': {'object_name': 'FormDefinition', '_ormbases': ['cms.CMSPlugin']},
u'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'email_from': ('django.db.models.fields.EmailField', [], {'max_length': '255', 'blank': 'True'}),
'email_subject': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'email_to': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'email_uploaded_files': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'external_redirect': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
'form_template': ('django.db.models.fields.CharField', [], {'default': "'djangocms_forms/form_template/default.html'", 'max_length': '150', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'page_redirect': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Page']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
'plugin_reference': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'plugin'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['djangocms_forms.Form']"}),
'post_submit_msg': ('django.db.models.fields.TextField', [], {'default': "u'Thank You'", 'blank': 'True'}),
'save_data': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'spam_protection': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
'submit_btn_txt': ('django.db.models.fields.CharField', [], {'default': "u'Submit'", 'max_length': '100'}),
'success_redirect': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '150', 'blank': 'True'})
},
u'djangocms_forms.formfield': {
'Meta': {'ordering': "('position',)", 'object_name': 'FormField'},
'choice_values': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'field_type': ('django.db.models.fields.CharField', [], {'default': "'text'", 'max_length': '100'}),
'form': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'fields'", 'to': u"orm['djangocms_forms.FormDefinition']"}),
'help_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'initial': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'placeholder_text': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
'position': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
'required': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'djangocms_forms.formsubmission': {
'Meta': {'ordering': "('-creation_date',)", 'object_name': 'FormSubmission'},
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True'}),
'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'form_data': ('jsonfield.fields.JSONField', [], {}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
'plugin': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'submissions'", 'to': u"orm['djangocms_forms.Form']"}),
'referrer': ('django.db.models.fields.CharField', [], {'max_length': '150', 'blank': 'True'})
},
u'sites.site': {
'Meta': {'ordering': "(u'domain',)", 'object_name': 'Site', 'db_table': "u'django_site'"},
'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
}
}
complete_apps = ['djangocms_forms']

@ -0,0 +1,43 @@
.inline-related {
border: 1px solid #CCC;
border-radius: 2px;
box-shadow: 0 1px 2px 1px rgba(0, 0, 0, 0.08);
}
.inline-related h3 {
background-color: #FCFCFC;
padding: 10px 20px 10px 3px;
margin: 0;
text-align: center;
}
.inline-related .inline_label {
font-weight: 300;
}
.inline-related .collapse-expand {
display: inline-block;
text-transform: uppercase;
padding: 0px 10px 0px 10px;
width: 44px;
float: left;
text-decoration:none!important;
}
.add-row {
height: 26px;
line-height: 26px;
text-align: center;
border: 1px dashed #CCC!important;
border-radius: 2px;
margin: 0;
}
.add-row a {
display: block;
padding-left: 14px;
text-indent: -9999px;
font-size: 11px;
background-position: 50% 50%!important;
}

@ -0,0 +1,53 @@
(function($) {
$(function() {
var formsubmission_form = $('#formsubmission_form');
var form_field = $('select[id="id_form"]', formsubmission_form);
var headers_field = $('.field-headers', formsubmission_form);
var loadHeaders = function() {
if (!form_field.val()) {
headers_field.slideUp('fast');
return;
}
var data = {
'csrfmiddlewaretoken': $('input[name="csrfmiddlewaretoken"]').val(),
'form': $('select[name="form"]').val(),
'file_type': $('select[name="file_type"]').val()
};
$.ajax({
type: 'POST',
url: '',
data: data,
success: function(response) {
if (response.reloadBrowser) {
location.reload();
}
$('select[id="id_headers_to"]').find('option').remove();
var headers_input_from = $('select[id="id_headers_from"]');
headers_input_from.find('option').remove();
$.each(response.availableHeaders, function(index, value) {
headers_input_from.append('<option value=' + value + '>' + value + '</option>');
});
SelectBox.init('id_headers_from');
SelectBox.init('id_headers_to');
headers_field.slideDown();
},
error: function() {
alert('We\'re sorry. Something unexpected happened. Please reload the page and try again.');
}
});
return false;
};
if ($('body').hasClass('export-form')) {
loadHeaders();
form_field.change(loadHeaders);
}
});
})(django.jQuery);

@ -0,0 +1,66 @@
/*
Makes all inline forms collapsible.
*/
jQuery(function($) {
$.makeCollapsible = function(target, item, collapsible, triggerTarget, setInitStatus, setFirstStatus)
{
var triggerExpand = 'Edit';
var triggerCollapse = 'Done';
var triggerClass = 'collapse-expand';
var triggerLink = '<a class="' + triggerClass + '" href="javascript:void(0);"></a>';
$(target).find(item).each(function(i) {
if ($(this).data('isCollapsible')) return;
$(this).data('isCollapsible', true);
$(this).find(collapsible).hide();
// trigger already exists if created with "Add another" link
var trigger = $(this).find(triggerTarget).find('.'+triggerClass);
if (!trigger.length) {
trigger = $(triggerLink);
$(this).find(triggerTarget).html(trigger);
}
var item = this;
var toggleCollapse = function(status, speed)
{
if (status == null) {
status = !item.collapseStatus;
}
if (speed == null) {
speed = 1;
}
item.collapseStatus = status;
if (status) {
trigger.html(triggerCollapse);
$(item).find(collapsible).slideDown('slow');
} else {
trigger.html(triggerExpand);
$(item).find(collapsible).slideUp('slow');
}
}
trigger.click(function(event) {
event.preventDefault();
toggleCollapse(null, 'normal')
})
// Collapse by default unless there are errors
initStatus = setInitStatus != null ? setInitStatus : $(this).find('.errors').length != 0;
firstStatus = setFirstStatus != null ? setFirstStatus : initStatus;
toggleCollapse(i == 0 ? firstStatus : initStatus)
});
};
var init = function() {
$.makeCollapsible('div.inline-group', 'div.inline-related', 'fieldset', 'h3 b');
};
init();
// init again when "Add another" link is clicked
$('.add-row a').click(function() {
init();
})
});

@ -0,0 +1,95 @@
/*
Enables repositioning of all inline elements by drag & drop.
The inline model requires is a "position" field that is blank by default.
This value will be set automatically by this code snippet when dragging elements.
The model instances can then be ordered by that "position" field.
*/
jQuery(function($) {
var positionField = 'position';
var target = $('div.inline-group#fields-group');
var handle = 'h3';
var item = 'div.inline-related';
var positionInput = 'input[id$=-'+positionField+']';
var hidePositionFieldClosest = '.form-row';
var renumberAll = function() {
var pos = 1;
target.find(item).each(function(i) {
$(this).removeClass('odd even').addClass(i % 2 ? 'even' : 'odd');
if ($(this).find(positionInput).val() != '') {
$(this).find(positionInput).val(pos);
pos++;
}
});
};
var init = function() {
target.find(item).each(function(i) {
if ($(this).data('isSortable')) return;
$(this).data('isSortable', true);
$(this).find(handle).css('cursor', 'ns-resize');
$(this).find(handle).addClass('draggable');
$(this).find(positionInput).each(function() {
if (hidePositionFieldClosest) {
var hidden =$('<input type="hidden" id="'+this.id+'" name="'+this.name+'" />');
hidden.val($(this).val());
$(this).closest(hidePositionFieldClosest).replaceWith(hidden);
}
});
$(this).find('input, select, textarea').change(function() {
$(this).closest(item).find('input[id$='+positionField+']').val('X'); // mark for renumberAll() to fill in
renumberAll($('div.inline-group'));
});
});
};
var addRow = target.find('.add-row');
addRow.remove();
var ordered = [];
var unordered = [];
// Initially, remove and re-append all inlines ordered by their "position" value
target.find(item).each(function(i) {
var initialPos = $(this).find(positionInput).val();
if (initialPos) {
while (initialPos < ordered.length && ordered[initialPos]) {
initialPos++;
}
ordered[initialPos] = this;
} else {
unordered[unordered.length] = this;
}
$(this).removeClass('odd even').addClass(i % 2 ? 'even' : 'odd');
this.parentElement.removeChild(this);
});
for (var i = 0; i < ordered.length; i++) {
var el = ordered[i];
if (el) {
target.append(el);
}
}
// Add "position"-less elements in the end
for (var i = 0; i < unordered.length; i++) {
var el = unordered[i];
target.append(el);
}
target.append(addRow);
target.sortable({
containment: 'parent',
items: item,
handle: handle,
update: renumberAll,
revert: true,
opacity: 0.9
});
init();
// init again when "Add another" link is clicked
$('.add-row a').click(function() {
init();
});
});

@ -0,0 +1,45 @@
/*
Replaces the name in an inline element's header while typing it in the input of the "name" field.
This way, the extra inline element's header will be named instead of numbered #4, #5 etc.
*/
jQuery(function($) {
var target = $('div.inline-group');
var item = 'div.inline-related';
var nameInput = 'input[id*=-label]';
var typeInput = 'select[id*=-field_type]';
var init = function() {
target.find(item).each(function() {
var nameField = $(this).find(nameInput);
var typeField = $(this).find(typeInput);
var label = $('.inline_label', this);
var rename = function() {
if (nameField.val()) {
label.text(nameField.val());
}
else {
label.text(typeField.find('option:selected').text() + ' Field');
}
};
nameField.keyup(function(event) {
// Update name while typing
rename();
});
typeField.change(function(){
rename();
});
rename();
})
}
init();
// init again when "Add another" link is clicked
$('.add-row a').click(function() {
init();
})
});

@ -0,0 +1,35 @@
jQuery(function($) {
var target = $('div.inline-group');
var item = 'div.inline-related';
var typeInput = 'select[id*=-field_type]';
var init = function() {
target.find(item).each(function() {
var that = $(this);
var inputField = that.find(typeInput);
var toggleField = function() {
var selectedFieldType = inputField.val();
var choiceFields = ['checkbox_multiple', 'select', 'radio', 'file'];
var showChoiceField = $.inArray(selectedFieldType, choiceFields) >= 0;
that.find('.field-choice_values')
.toggle(showChoiceField)
.toggleClass('required', showChoiceField);
that.find('.field-placeholder_text, .field-help_text, .field-choice_values:not(:hidden)')
.toggle(selectedFieldType != 'hidden')
};
inputField.change(function() {
toggleField();
});
toggleField();
})
};
init();
// init again when "Add another" link is clicked
$('.add-row a').click(function() {
init();
})
});

@ -0,0 +1,100 @@
(function ($) {
$.fn.djangocms_forms = function (options) {
if (options === undefined) { options = {}; }
var defaults = {
form_wrapper: '.form-wrapper',
field_wrapper: '.field-wrapper',
form_errors: '.form-errors',
field_errors: '.field-errors',
form_success: '.form-success',
errorlist_css_class: 'errorlist',
error_css_class: 'error',
server_error: 'We\'re sorry. Something Unexpected Happened. Please Try Again Later.'
};
this.each(function(options) {
var options = $.extend( {}, defaults, options) ;
var el = $(this),
form_wrapper = $(options.form_wrapper, el),
form_success = $(options.form_success, el),
form = $('form', form_wrapper);
function clearErrors() {
form.find(options.form_errors).fadeOut().empty(); //clear form errors
form.find(options.field_errors).fadeOut().empty(); //clear field errors
form.find(options.field_wrapper).removeClass(options.error_css_class); //remove error classes
}
// post-submit callback
function ajaxSuccess(response) {
if (response.status == 'success'){
formValid(response.redirect_url);
}
else if (response.status == 'error'){
formInvalid(response.form_errors);
}
}
function formValid(success_url) {
form_success.fadeIn('slow');
form_wrapper.slideUp('slow').remove();
if (success_url){
setTimeout(function(){
window.location = success_url;
}, 1000);
}
}
function formInvalid(form_errors) {
clearErrors()
$.each(form_errors, function(key, value) {
var field = form.find(':input[name=' + key + ']').first();
var field_wrapper = field.parents(options.field_wrapper).addClass(options.error_css_class);
var field_error = $('<ul/>').addClass(options.errorlist_css_class);
$.each(value, function(key, value) {
$('<li>', {
text: value
}).appendTo(field_error);
});
field_wrapper.find(options.field_errors).append(field_error).fadeIn('slow')
});
if (form_errors.__all__) {
var form_error = $('<ul/>').addClass(options.errorlist_css_class);
$.each(form_errors.__all__, function(key, value) {
$('<li>', {
text: value
}).appendTo(form_error);
});
form.find(options.form_errors).append(form_error).fadeIn('slow');
}
}
function ajaxError() {
clearErrors();
var form_error = $('<ul/>').addClass(options.error_class);
$('<li>', {
html: options.server_error
}).appendTo(form_error);
form.find(options.form_errors).append(form_error).fadeIn('slow');
}
// attach handler to form's submit event
form.submit(function () {
var ajaxOptions = {
type: 'POST',
success: ajaxSuccess,
error: ajaxError
};
// submit the form
$(this).ajaxSubmit(ajaxOptions);
// return false to prevent normal browser submit and page navigation
return false;
});
});
};
})(jQuery);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,26 @@
{% extends "admin/change_form.html" %}
{% load i18n djangocms_forms_tags %}
{% block submit_buttons_bottom %}{% endblock %}
{% block object-tools-items %}
{% endblock %}
{% block field_sets %}
{% if original.form_data %}
<fieldset class="module aligned">
{% for field in original.form_data %}
<div class="form-row required {{ field.name}}">
<div>
<label>
{{ field.label }}:
</label>
<p>{{ field|to_html }}</p>
</div>
</div>
{% endfor %}
</fieldset>
{% endif %}
{{ block.super }}
{% endblock %}

@ -0,0 +1,15 @@
{% extends "admin/change_list.html" %}
{% load i18n admin_urls %}
{% block object-tools %}
{% if has_export_permission %}
<ul class="object-tools">
{% block object-tools-items %}
<li>
{% url cl.opts|admin_urlname:'export' as export_url %}
<a href="{{ export_url }}" class="addlink">{% trans "Export Form Submissions" %}</a>
</li>
{% endblock %}
</ul>
{% endif %}
{% endblock %}

@ -0,0 +1,22 @@
{% extends "admin/change_form.html" %}
{% load i18n admin_urls admin_static admin_modify %}
{% block bodyclass %}{{ block.super }} export-form{% endblock %}
{% if not is_popup %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
&rsaquo; {% if has_change_permission %}<a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>{% else %}{{ opts.verbose_name_plural|capfirst }}{% endif %}
&rsaquo; {% trans 'Export' %} {{ opts.verbose_name|capfirst }}
</div>
{% endblock %}
{% endif %}
{% block submit_buttons_bottom %}
<div class="submit-row">
<input type="submit" value="{% trans 'Export' %}" class="default" name="export" />
</div>
{% endblock %}

@ -0,0 +1,19 @@
{% load i18n djangocms_forms_tags %}<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>{{ title }}</title>
</head>
<body itemscope itemtype="http://schema.org/EmailMessage">
<h1>{{ form.title }}</h1>
<p><strong>{% trans 'Date:' %}</strong> {% now 'jS F Y H:i' %}</p>
{% for item in form_data %}
<p>
<strong>{% if item.label %}{{ item.label }}{% else %}{{ item.name }}{% endif %}:</strong><br/>
{{ item.value|friendly|default_if_none:'—' }}
</p>
{% endfor %}
{% trans 'Sent via' %} <a href="http://{{ request.get_host }}{{ referrer }}">{{ request.get_host }}{{ referrer }}</a>
</body>
</html>

@ -0,0 +1,11 @@
{% load i18n djangocms_forms_tags %}
{% trans 'Form:' %} {{ form.name }}
{% trans 'Date:' %} {% now "jS F Y H:i" %}
{% for item in form_data %}
{% if item.label %}{{ item.label }}{% else %}{{ item.name }}{% endif %}:
{{ item.value|friendly|default_if_none:'—' }}
{% endfor %}
{% trans 'Sent via' %} http://{{ request.get_host }}

@ -0,0 +1,61 @@
{% load cms_tags djangocms_forms_tags i18n sekizai_tags staticfiles %}
<div class="forms" id="{{ instance.form_id }}">
{% if instance.title %}
<h3 class="title">{{ instance.title }}</h3>
{% endif %}
{% if instance.description %}
<div class="description">
{{ instance.description|safe }}
</div>
{% endif %}
<div class="form-wrapper">
<form action="{{ form.submission_url }}" method="POST" enctype="multipart/form-data">
<div class="form-errors" style="display:none;"></div>
{% for field in form.visible_fields %}
<div class="field-wrapper {{ field|input_class }} {{ field.css_classes }}">
<div class="field-errors" style="display:none;"></div>
{% if field|is_checkbox %}
{{ field }}
{% endif %}
<label for="{{ field.id_for_label }}">
{{ field.label }}
</label>
{% if not field|is_checkbox %}
<p>{{ field }}</p>
{% endif %}
{% if field.help_text %}
<div id="help-text-{{ field.auto_id }}" class="help-text">{{ field.help_text|safe }}</div>
{% endif %}
</div>
{% endfor %}
{% csrf_token %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
<div class="button-wrapper submit">
<button class="form-button btn btn-primary" type="submit" value="{{ instance.submit_btn_txt }}">{{ instance.submit_btn_txt }}</button>
</div>
</form>
</div>
<div class="form-success" style="display: none;">
{% if instance.post_submit_msg %}
<p>{{ instance.post_submit_msg|safe }}</p>
{% else %}
{% blocktrans %}
<h3>Submission successful</h3>
<p>Thank You! Your form has been successfully submitted!</p>
{% endblocktrans %}
{% endif %}
</div>
</div>
{% addtoblock "js" %}
<script src="{% static 'js/djangocms_forms/libs/jquery.form.min.js' %}"></script>
<script src="{% static 'js/djangocms_forms/djangocms_forms.js' %}"></script>
<script type="application/javascript">
$(function() {
$('.forms').djangocms_forms();
});
</script>
{% endaddtoblock %}

@ -0,0 +1,89 @@
from django import forms, template
from django.template.defaultfilters import yesno
from django.utils.safestring import mark_safe
from django.utils.six import string_types
from django.utils.translation import ugettext_lazy as _
register = template.Library()
@register.filter
def is_checkbox(field):
return isinstance(field.field.widget, forms.CheckboxInput)
@register.filter
def is_password(field):
return isinstance(field.field.widget, forms.PasswordInput)
@register.filter
def is_radioselect(field):
return isinstance(field.field.widget, forms.RadioSelect)
@register.filter
def is_select(field):
return isinstance(field.field.widget, forms.Select)
@register.filter
def is_checkboxselectmultiple(field):
return isinstance(field.field.widget, forms.CheckboxSelectMultiple)
@register.filter
def is_file(field):
return isinstance(field.field.widget, forms.ClearableFileInput)
@register.filter
def is_required(field):
return field.field.required
@register.filter
def classes(field):
"""
Returns CSS classes of a field
"""
return field.widget.attrs.get('class', None)
@register.filter
def input_class(field):
"""
Returns widgets class name in lowercase
"""
return field.field.widget.__class__.__name__.lower()
@register.filter
def friendly(value):
if value in (None, '', [], (), {}):
return None
if type(value) is list:
value = ', '.join(value)
if type(value) is bool:
value = yesno(value, u'{0},{1}'.format(_('Yes'), _('No')))
if not isinstance(value, string_types):
value = unicode(value)
return value
@register.filter
def to_html(field):
value = field['value']
field_type = field['type']
if value in (None, '', [], (), {}):
return mark_safe('&mdash;')
if field_type == 'file':
value = '<a href="{0}">{0}</a>'.format(value)
if field_type == 'checkbox':
value = yesno(bool(value), u'{0},{1}'.format(_('Yes'), _('No')))
if field_type == 'checkbox_multiple':
value = ', '.join(list(value))
return mark_safe(value)

@ -0,0 +1,62 @@
import hashlib
import os
import uuid
from django.core.files.base import File
from django.core.files.storage import get_storage_class
from django.db.models.fields.files import FieldFile
from django.utils.functional import LazyObject
from .conf import settings
class FileStorage(LazyObject):
def _setup(self):
self._wrapped = get_storage_class(settings.DJANGOCMS_FORMS_FILE_STORAGE)()
file_storage = FileStorage()
def handle_uploaded_files(form):
files = []
if len(form.file_fields):
secret_hash = hashlib.sha1(str(uuid.uuid4())).hexdigest()
for field in form.file_fields:
uploaded_file = form.cleaned_data.get(field, None)
if uploaded_file is None:
continue
valid_file_name = file_storage.get_valid_name(uploaded_file.name)
root, ext = os.path.splitext(valid_file_name)
filename = file_storage.get_available_name(os.path.join(
settings.DJANGOCMS_FORMS_FILE_STORAGE_DIR,
form.form_definition.upload_to,
'%s_%s%s' % (root, secret_hash, ext)))
file_storage.save(filename, uploaded_file)
form.cleaned_data[field] = StoredUploadedFile(filename)
files.append(file_storage.path(filename))
return files
class StoredUploadedFile(FieldFile):
"""
A wrapper for uploaded files that is compatible to the FieldFile class, i.e.
you can use instances of this class in templates just like you use the value
of FileFields (e.g. `{{ my_file.url }}`)
"""
def __init__(self, name):
File.__init__(self, None, name)
self.field = self
@property
def storage(self):
return file_storage
def save(self, *args, **kwargs):
raise NotImplementedError('Static files are read-only')
def delete(self, *args, **kwargs):
raise NotImplementedError('Static files are read-only')
def __unicode__(self):
return self.name

@ -0,0 +1,8 @@
from django.conf.urls import patterns, url
from .views import FormSubmission
urlpatterns = patterns(
'',
url(r'^forms/submit/$', FormSubmission.as_view(), name='djangocms_forms_submissions'),
)

@ -0,0 +1,17 @@
from hashids import Hashids
from .conf import settings
def int_to_hashid(i, min_length=30, salt=settings.DJANGOCMS_FORMS_HASHIDS_SALT):
hashids = Hashids(salt, min_length=min_length)
return hashids.encode(i)
def hashid_to_int(hashid, min_length=30, salt=settings.DJANGOCMS_FORMS_HASHIDS_SALT):
hashids = Hashids(salt, min_length=min_length)
try:
return hashids.decode(hashid)[0]
except IndexError:
pass

@ -0,0 +1,61 @@
# -*- coding: utf-8 -*-
from django.contrib import messages
from django.shortcuts import get_object_or_404, redirect
from django.utils.html import strip_tags
from django.utils.translation import ugettext_lazy as _
from django.views.generic import FormView
from .forms import FormBuilder
from .models import FormDefinition
from .signals import form_submission
from .uploads import handle_uploaded_files
from .utils import hashid_to_int
try:
from django.http import JsonResponse
except ImportError:
from .compat import JsonResponse
class FormSubmission(FormView):
form_class = FormBuilder
http_method_names = ['post']
def get_form_kwargs(self):
form_kwargs = super(FormSubmission, self).get_form_kwargs()
form_id = hashid_to_int(self.request.POST.get('form_id'))
form_definition = get_object_or_404(FormDefinition, pk=form_id)
form_kwargs.update({
'form_definition': form_definition
})
return form_kwargs
def form_valid(self, form, *args, **kwargs):
form.files = handle_uploaded_files(form)
form.save(request=self.request)
form_submission.send(
sender=self.__class__,
form=form.form_definition,
cleaned_data=form.cleaned_data)
if self.request.is_ajax():
response = {
'status': 'success',
'redirect_url': form.redirect_url
}
return JsonResponse(response)
else:
messages.success(self.request, strip_tags(form.form_definition.post_submit_msg))
return redirect(form.redirect_url or form.cleaned_data['current_page'])
def form_invalid(self, form, *args, **kwargs):
if self.request.is_ajax():
response = {
'status': 'error',
'form_errors': form.errors,
}
return JsonResponse(response)
else:
messages.error(self.request, _(u'Invalid form data, one or more fields had errors'))
return redirect(form.cleaned_data['current_page'])

@ -0,0 +1,17 @@
from django.forms.widgets import TextInput
class TelephoneInput(TextInput):
input_type = 'tel'
class SearchInput(TextInput):
input_type = 'search'
class DateInput(TextInput):
input_type = 'date'
class TimeInput(TextInput):
input_type = 'time'

@ -11,4 +11,18 @@
font-size: 33px;
vertical-align: text-bottom;
}
}
.field-wrapper {
margin: 10px 0;
ul {
list-style: none;
margin: 0;
padding: 0;
label {
font-weight: 100;
}
}
}

@ -60,9 +60,7 @@
</div>
<div class="menu">
<h5><nobr>{% trans "— There is nothing impossible. Almost nothing" %}</nobr></h5>
<ul class="list-unstyled">
{% show_menu 0 1 100 100 "menu.html" %}
</ul>
{% show_menu 0 1 100 100 "menu_footer.html" %}
</div>
<div class="contacts pull-right">
<ul class="list-unstyled">

@ -0,0 +1,14 @@
{% load i18n cms_tags menu_tags cache %}
{{ children.count }}
{% if children|length > 1 %}
<ul class="list-unstyled">
{% for child in children %}
<li {% if child.selected %}class="active"{% endif %}>
<a href="{{ child.get_absolute_url }}"><span>{{ child.get_menu_title }}</span></a>
</li>
{% if class and forloop.last and not forloop.parentloop %}{% endif %}
{% endfor %}
</ul>
{% else %}
{% static_placeholder 'footer_promo' %}
{% endif %}
Loading…
Cancel
Save