diff --git a/wagtail/wagtailforms/forms.py b/wagtail/wagtailforms/forms.py new file mode 100644 index 0000000000..b444895c58 --- /dev/null +++ b/wagtail/wagtailforms/forms.py @@ -0,0 +1,67 @@ +import django.forms +from django.utils.datastructures import SortedDict +from django.utils.text import slugify +from unidecode import unidecode + +class FormBuilder(): + formfields = SortedDict() + def __init__(self, fields): + for field in fields: + options = self.get_options(field) + f = getattr(self, "create_"+field.field_type+"_field" )(field, options) + # unidecode will return an ascii string while slugify wants a unicode string + # on the other hand, slugify returns a safe-string which will be converted + # to a normal str + field_name = str(slugify(unicode(unidecode(field.label)))) + self.formfields[field_name] = f + + def get_options(self, field): + options = {} + options['label'] = field.label + options['help_text'] = field.help_text + options['required'] = field.required + options['initial'] = field.default_value + return options + + def create_singleline_field(self, field, options): + # TODO: This is a default value - it may need to be changed + options['max_length'] = 255 + return django.forms.CharField(**options) + + def create_multiline_field(self, field, options): + return django.forms.CharField(widget=django.forms.Textarea, **options) + + def create_date_field(self, field, options): + return django.forms.DateField(**options) + + def create_datetime_field(self, field, options): + return django.forms.DateTimeField(**options) + + def create_email_field(self, field, options): + return django.forms.EmailField(**options) + + def create_url_field(self, field, options): + return django.forms.URLField(**options) + + def create_number_field(self, field, options): + return django.forms.DecimalField(**options) + + def create_dropdown_field(self, field, options): + options['choices'] = map(lambda x: (x.strip(),x.strip()), field.choices.split(',')) + return django.forms.ChoiceField(**options) + + def create_radio_field(self, field, options): + options['choices'] = map(lambda x: (x.strip(),x.strip()), field.choices.split(',')) + return django.forms.ChoiceField(widget=django.forms.RadioSelect, **options) + + def create_checkboxes_field(self, field, options): + options['choices'] = map(lambda x: (x.strip(),x.strip()), field.choices.split(',')) + options['initial'] = field.default_value.split(',') + return django.forms.MultipleChoiceField(widget=django.forms.CheckboxSelectMultiple, **options) + + def create_checkbox_field(self, field, options): + return django.forms.BooleanField(**options) + + def get_form_class(self): + return type('WagtailForm', (django.forms.Form,), self.formfields ) + diff --git a/wagtail/wagtailforms/models.py b/wagtail/wagtailforms/models.py index e4be5a3790..ff758497c3 100644 --- a/wagtail/wagtailforms/models.py +++ b/wagtail/wagtailforms/models.py @@ -1,32 +1,53 @@ +from django.conf import settings from django.db import models from django.shortcuts import render from django.utils.translation import ugettext_lazy as _ +import json +import re + from wagtail.wagtailcore.models import Page, Orderable from wagtail.wagtailadmin.edit_handlers import FieldPanel, InlinePanel from modelcluster.fields import ParentalKey +from .forms import FormBuilder + FORM_FIELD_CHOICES = ( - ('SINGLELINE', _('Single line text')), - ('MULTILINE', _('Multi-line text')), - ('EMAIL', _('Email')), - ('NUMBER', _('Number')), - ('URL', _('URL')), - ('CHECKBOX', _('Checkbox')), - ('CHECKBOXES', _('Checkboxes')), - ('DROPDOWN', _('Drop down')), - ('RADIO', _('Radio buttons')), - ('DATE', _('Date')), - ('DATETIME', _('Date/time')), + ('singleline', _('Single line text')), + ('multiline', _('Multi-line text')), + ('email', _('Email')), + ('number', _('Number')), + ('url', _('URL')), + ('checkbox', _('Checkbox')), + ('checkboxes', _('Checkboxes')), + ('dropdown', _('Drop down')), + ('radio', _('Radio buttons')), + ('date', _('Date')), + ('datetime', _('Date/time')), ) + +HTML_EXTENSION_RE = re.compile(r"(.*)\.html") + + +class FormSubmission(models.Model): + """Data for a Form submission.""" + form_data = models.TextField() + form_page = models.ForeignKey('wagtailcore.Page',related_name='+') + submit_time = models.DateTimeField(auto_now_add=True) + user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True) + + def __unicode__(self): + return self.form_data + class AbstractFormFields(models.Model): - #page = ParentalKey('wagtailforms.AbstractForm', related_name='form_fields') - label = models.CharField(max_length=255) + """Database Fields required for building a Django Form field.""" + + label = models.CharField(max_length=255, help_text=_('The label of the form field') ) field_type = models.CharField(max_length=16, choices = FORM_FIELD_CHOICES) - required = models.BooleanField( default=True) - choices = models.CharField(max_length=512, blank=True, help_text='Comma seperated list of choices') + required = models.BooleanField(default=True) + choices = models.CharField(max_length=512, blank=True, help_text=_('Comma seperated list of choices')) default_value = models.CharField(max_length=255, blank=True) help_text = models.CharField(max_length=255, blank=True) @@ -42,31 +63,64 @@ class AbstractFormFields(models.Model): class Meta: abstract = True - - + class AbstractForm(Page): - is_abstract = True #Don't display me in "Add" + """A Form Page. Pages with form should inhert from it""" + form_builder = FormBuilder + is_abstract = True # Don't display me in "Add" + + def __init__(self, *args, **kwargs): + super(Page, self).__init__(*args, **kwargs) + if not hasattr(self, 'landing_page_template'): + template_wo_ext = re.match(HTML_EXTENSION_RE, self.template).group(1) + self.landing_page_template = template_wo_ext + '_landing.html' class Meta: abstract = True - + def serve(self, request): - # Get fields - form_fields = self.form_fields + fb = self.form_builder(self.form_fields.all() ) + form_class = fb.get_form_class() + + if request.method == 'POST': + self.form = form_class(request.POST) + + if self.form.is_valid(): + # remove csrf_token from form.data + form_data = dict( + i for i in self.form.data.items() + if i[0] != 'csrfmiddlewaretoken' + ) + FormSubmission.objects.create( + form_data = json.dumps(form_data), + form_page = self.page_ptr, + user = request.user, + ) + # TODO: Do other things like sending email + # render the landing_page + # TODO: It is much better to redirect to it + return render(request, self.landing_page_template, { + 'self': self, + }) + else: + self.form = form_class() return render(request, self.template, { 'self': self, + 'form': self.form, }) - + + +######## TEST class ConcreteFormFields(Orderable, AbstractFormFields): page = ParentalKey('wagtailforms.ConcreteForm', related_name='form_fields') - class ConcreteForm(AbstractForm): - pass + thank_you = models.CharField(max_length=255) ConcreteForm.content_panels = [ FieldPanel('title', classname="full title"), + FieldPanel('thank_you', classname="full"), InlinePanel(ConcreteForm, 'form_fields', label="Form Fields"), ]