diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..d76d3cfa
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+*.pyc
+.*
+dist
+MANIFEST
+
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index b72179b6..00000000
--- a/.gitmodules
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "form_designer/media/form_designer/js/lib/django-admin-tweaks-js-lib"]
- path = form_designer/media/form_designer/js/lib/django-admin-tweaks-js-lib
- url = git://github.com/philomat/django-admin-tweaks-js-lib.git
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 00000000..98e5b938
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,2 @@
+Samuel Lüscher (philomat)
+Jannis Leidel (jezdez)
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 00000000..1d99ea08
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,15 @@
+<<<<<<< HEAD
+include LICENSE
+include README.md
+
+recursive-include form_designer/locale *
+recursive-include form_designer/media *
+recursive-include form_designer/templates *
+=======
+include AUTHORS
+include README.md
+include LICENSE
+recursive-include form_designer/templates *.html *.txt
+recursive-include form_designer/media *.js
+recursive-include form_designer/locale *.po *.mo
+>>>>>>> 29a8207f09ef53dba714edf377d444702106ad5c
diff --git a/README.md b/README.md
index 4cee4a27..c9320546 100644
--- a/README.md
+++ b/README.md
@@ -29,18 +29,19 @@ This document assumes that you are familiar with Python and Django.
2. Make sure `form_designer` is on your `PYTHONPATH`.
3. Make the directory `form_designer/media/form_designer` available under your `MEDIA_ROOT`.
-4. Set up the database tables using
-
- $ manage.py syncdb
-5. Add `form_designer` to your `INSTALLED_APPS` setting.
+4. Add `form_designer` to your `INSTALLED_APPS` setting.
INSTALLED_APPS = (
...
'form_designer',
)
-6. Add the form_designer URLs to your URL conf. For instance, in order to make a form named `example-form` available under `http://domain.com/forms/example-form`, add the following line to `urls.py`. Note: __If you are using the form_designer plugin for Django CMS, step 5 is not necessary__:
+5. Set up the database tables using
+
+ $ manage.py syncdb
+
+6. Add the form_designer URLs to your URL conf. For instance, in order to make a form named `example-form` available under `http://domain.com/forms/example-form`, add the following line to `urls.py`. Note: __If you are using the form_designer plugin for Django CMS, this step is not necessary__:
urlpatterns = patterns('',
(r'^forms/', include('form_designer.urls')),
@@ -62,8 +63,3 @@ Optional requirements
* The form_designer admin interface requires jQuery and the jQuery UI Sortable plugin to make building forms a lot more user-friendly. The two Javascript files are bundled with form_designer. Optionally, if Django CMS is installed, the files bundled with that app will be used. If you want to use you own jquery.js instead because you're already including it anyway, define JQUERY_JS in your settings file. For instance:
JQUERY_JS = 'jquery/jquery-latest.js'
-
-Missing features
-----------------
-
-* File upload fields should be implemented
diff --git a/form_designer/__init__.py b/form_designer/__init__.py
index e69de29b..0e6c49cb 100644
--- a/form_designer/__init__.py
+++ b/form_designer/__init__.py
@@ -0,0 +1,7 @@
+from south.modelsinspector import add_introspection_rules
+
+
+add_introspection_rules([], ['^form_designer\.pickled_object_field\.PickledObjectField'])
+add_introspection_rules([], ['^form_designer\.model_name_field\.ModelNameField'])
+add_introspection_rules([], ['^form_designer\.template_field\.TemplateCharField'])
+add_introspection_rules([], ['^form_designer\.template_field\.TemplateTextField'])
diff --git a/form_designer/admin.py b/form_designer/admin.py
index ff13860b..77b6b34c 100644
--- a/form_designer/admin.py
+++ b/form_designer/admin.py
@@ -1,21 +1,23 @@
+import csv
from django.contrib import admin
+from django.utils.translation import ugettext as _, ugettext_lazy
+from django.conf.urls.defaults import patterns, url
+from django.contrib.admin.views.main import ChangeList
+from django.db.models import Count
+from django.http import HttpResponse
+from django.utils.encoding import smart_unicode, smart_str
+
+try:
+ import xlwt
+except ImportError:
+ xlwt_installed = False
+else:
+ xlwt_installed = True
+
+from form_designer.forms import FormDefinitionForm, FormDefinitionFieldInlineForm
from form_designer.models import FormDefinition, FormDefinitionField, FormLog
-from django import forms
-from django.utils.translation import ugettext as _
-from django.db import models
-from django.conf import settings
-import os
-
-MEDIA_SUBDIR = 'form_designer'
-
-class FormDefinitionFieldInlineForm(forms.ModelForm):
- class Meta:
- model = FormDefinitionField
-
- def clean_choice_model(self):
- if not self.cleaned_data['choice_model'] and self.cleaned_data.has_key('field_class') and self.cleaned_data['field_class'] in ('forms.ModelChoiceField', 'forms.ModelMultipleChoiceField'):
- raise forms.ValidationError(_('This field class requires a model.'))
- return self.cleaned_data['choice_model']
+from form_designer import settings
+from form_designer.templatetags.friendly import friendly
class FormDefinitionFieldInline(admin.StackedInline):
form = FormDefinitionFieldInlineForm
@@ -31,34 +33,6 @@ class FormDefinitionFieldInline(admin.StackedInline):
(_('Model Choices'), {'fields': ['choice_model', 'choice_model_empty_label']}),
]
-class FormDefinitionForm(forms.ModelForm):
- class Meta:
- model = FormDefinition
- class Media:
- js = ([
- # Use central jQuery
- settings.JQUERY_JS,
- # and use jQuery UI bundled with this app
- os.path.join(MEDIA_SUBDIR, 'lib/jquery/ui.core.js'),
- os.path.join(MEDIA_SUBDIR, 'lib/jquery/ui.sortable.js'),
- ] if hasattr(settings, 'JQUERY_JS') else [
- # Use jQuery bundled with CMS
- os.path.join(settings.CMS_MEDIA_URL, 'js/lib/jquery.js'),
- os.path.join(settings.CMS_MEDIA_URL, 'js/lib/ui.core.js'),
- os.path.join(settings.CMS_MEDIA_URL, 'js/lib/ui.sortable.js'),
- ] if hasattr(settings, 'CMS_MEDIA_URL') else [
- # or use jQuery bundled with this app
- os.path.join(MEDIA_SUBDIR, 'lib/jquery/jquery.js'),
- os.path.join(MEDIA_SUBDIR, 'lib/jquery/ui.core.js'),
- os.path.join(MEDIA_SUBDIR, 'lib/jquery/ui.sortable.js'),
- ])+[os.path.join(MEDIA_SUBDIR, 'js/lib/django-admin-tweaks-js-lib/js', basename) for basename in (
- 'jquery-inline-positioning.js',
- 'jquery-inline-rename.js',
- 'jquery-inline-collapsible.js',
- 'jquery-inline-fieldset-collapsible.js',
- 'jquery-inline-prepopulate-label.js',
- )]
-
class FormDefinitionAdmin(admin.ModelAdmin):
fieldsets = [
(_('Basic'), {'fields': ['name', 'method', 'action', 'title', 'allow_get_initial', 'log_data', 'success_redirect', 'success_clear']}),
@@ -76,7 +50,10 @@ class FormLogAdmin(admin.ModelAdmin):
list_display = ('form_no_link', 'created', 'id', 'data_html')
list_filter = ('form_definition',)
list_display_links = ()
-
+ actions = ['export_csv']
+ if xlwt_installed:
+ actions.append('export_xls')
+
# Disabling all edit links: Hack as found at http://stackoverflow.com/questions/1618728/disable-link-to-edit-object-in-djangos-admin-display-list-only
def form_no_link(self, obj):
return ''+obj.form_definition.__unicode__()+''
@@ -84,25 +61,133 @@ def form_no_link(self, obj):
form_no_link.allow_tags = True
form_no_link.short_description = _('Form')
+ def get_urls(self):
+ urls = patterns('',
+ url(r'^export/csv/$', self.admin_site.admin_view(self.export_csv), name='form_designer_export_csv'),
+ )
+ if xlwt_installed:
+ urls += patterns('',
+ url(r'^export/xls/$', self.admin_site.admin_view(self.export_xls), name='form_designer_export_xls'),
+ )
+ return urls + super(FormLogAdmin, self).get_urls()
+
def data_html(self, obj):
return obj.form_definition.compile_message(obj.data, 'html/formdefinition/data_message.html')
data_html.allow_tags = True
data_html.short_description = _('Data')
+ def get_change_list_query_set(self, request):
+ cl = ChangeList(request, self.model, self.list_display, self.list_display_links, self.list_filter,
+ self.date_hierarchy, self.search_fields, self.list_select_related, self.list_per_page, self.list_editable, self)
+ return cl.get_query_set()
+
+ def export_csv(self, request, queryset=None):
+ response = HttpResponse(mimetype='text/csv')
+ response['Content-Disposition'] = 'attachment; filename=' + settings.CSV_EXPORT_FILENAME
+ writer = csv.writer(response, delimiter=settings.CSV_EXPORT_DELIMITER)
+ if queryset is None:
+ queryset = self.get_change_list_query_set(request)
+
+ distinct_forms = queryset.aggregate(Count('form_definition', distinct=True))['form_definition__count']
+
+ include_created = settings.CSV_EXPORT_INCLUDE_CREATED
+ include_pk = settings.CSV_EXPORT_INCLUDE_PK
+ include_header = settings.CSV_EXPORT_INCLUDE_HEADER and distinct_forms == 1
+ include_form = settings.CSV_EXPORT_INCLUDE_FORM and distinct_forms > 1
+
+ if include_header:
+ header = []
+ if include_form:
+ header.append(_('Form'))
+ if include_created:
+ header.append(_('Created'))
+ if include_pk:
+ header.append(_('ID'))
+ for field in queryset[0].data:
+ header.append(field['label'] if field['label'] else field['key'])
+ writer.writerow([smart_str(cell, encoding=settings.CSV_EXPORT_ENCODING) for cell in header])
+
+ for entry in queryset:
+ row = []
+ if include_form:
+ row.append(entry.form_definition)
+ if include_created:
+ row.append(entry.created)
+ if include_pk:
+ row.append(entry.pk)
+ for field in entry.data:
+ value = friendly(field['value'])
+ row.append(smart_str(
+ value, encoding=settings.CSV_EXPORT_ENCODING))
+ writer.writerow(row)
+ return response
+ export_csv.short_description = ugettext_lazy("Export selected %(verbose_name_plural)s as CSV")
+
+ def export_xls(self, request, queryset=None):
+ import xlwt
+
+ response = HttpResponse(mimetype='application/ms-excel')
+ response['Content-Disposition'] = 'attachment; filename=%s.xls' % unicode(self.model._meta.verbose_name_plural)
+ wb = xlwt.Workbook()
+ ws = wb.add_sheet(unicode(self.model._meta.verbose_name_plural))
+ if queryset is None:
+ queryset = self.get_change_list_query_set(request)
+
+ distinct_forms = queryset.aggregate(Count('form_definition', distinct=True))['form_definition__count']
+
+ include_created = settings.CSV_EXPORT_INCLUDE_CREATED
+ include_pk = settings.CSV_EXPORT_INCLUDE_PK
+ include_header = settings.CSV_EXPORT_INCLUDE_HEADER and distinct_forms == 1
+ include_form = settings.CSV_EXPORT_INCLUDE_FORM and distinct_forms > 1
+
+ if include_header:
+ header = []
+ if include_form:
+ header.append(_('Form'))
+ if include_created:
+ header.append(_('Created'))
+ if include_pk:
+ header.append(_('ID'))
+ for field in queryset[0].data:
+ header.append(field['label'] if field['label'] else field['key'])
+ for i, f in enumerate(header):
+ ws.write(0, i, smart_unicode(f, encoding=settings.CSV_EXPORT_ENCODING))
+
+ for i, entry in enumerate(queryset):
+ row = []
+ if include_form:
+ row.append(entry.form_definition)
+ if include_created:
+ row.append(entry.created)
+ if include_pk:
+ row.append(entry.pk)
+ for field in entry.data:
+ value = friendly(field['value'])
+ row.append(smart_unicode(
+ value, encoding=settings.CSV_EXPORT_ENCODING))
+ for j, cell in enumerate(row):
+ ws.write(i+1, j, smart_unicode(cell))
+ wb.save(response)
+ return response
+ export_xls.short_description = ugettext_lazy("Export selected %(verbose_name_plural)s as XLS")
+
def changelist_view(self, request, extra_context=None):
- from django.core.urlresolvers import reverse, NoReverseMatch
+ from django.core.urlresolvers import reverse, NoReverseMatch
extra_context = extra_context or {}
try:
query_string = '?'+request.META['QUERY_STRING']
- except TypeError, KeyError:
+ except (TypeError, KeyError):
query_string = ''
try:
- extra_context['export_csv_url'] = reverse('form_designer_export_csv')+query_string
+ extra_context['export_csv_url'] = reverse('admin:form_designer_export_csv')+query_string
except NoReverseMatch:
request.user.message_set.create(message=_('CSV export is not enabled.'))
-
+ if xlwt_installed:
+ try:
+ extra_context['export_xls_url'] = reverse('admin:form_designer_export_xls')+query_string
+ except NoReverseMatch:
+ request.user.message_set.create(message=_('XLS export is not enabled.'))
return super(FormLogAdmin, self).changelist_view(request, extra_context)
admin.site.register(FormDefinition, FormDefinitionAdmin)
admin.site.register(FormLog, FormLogAdmin)
-
diff --git a/form_designer/admin_urls.py b/form_designer/admin_urls.py
deleted file mode 100644
index 59173260..00000000
--- a/form_designer/admin_urls.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from django.conf.urls.defaults import *
-
-urlpatterns = patterns('',
-
- url(r'^formlog/export_csv/$', 'form_designer.admin_views.export_csv', name='form_designer_export_csv'),
-
-)
diff --git a/form_designer/admin_views.py b/form_designer/admin_views.py
deleted file mode 100644
index 923ead33..00000000
--- a/form_designer/admin_views.py
+++ /dev/null
@@ -1,62 +0,0 @@
-# encoding=utf-8
-from django.http import HttpResponse
-from form_designer.models import FormLog
-from form_designer.admin import FormLogAdmin
-from form_designer import app_settings
-from django.utils.translation import ugettext as _
-from form_designer.templatetags.friendly import friendly
-from django.conf import settings
-import csv
-
-# Returns a QuerySet with the same ordering and filtering like the one that would be visible in Django admin
-def get_change_list_query_set(model_admin, model, request):
- from django.contrib import admin
- from django.contrib.admin.views.main import ChangeList
- a = model_admin(model, admin.site)
- cl = ChangeList(request, a.model, a.list_display, a.list_display_links, a.list_filter,
- a.date_hierarchy, a.search_fields, a.list_select_related, a.list_per_page, a.list_editable, a)
- return cl.get_query_set()
-
-def export_csv(request):
- response = HttpResponse(mimetype='text/csv')
- response['Content-Disposition'] = 'attachment; filename='+app_settings.get('FORM_DESIGNER_CSV_EXPORT_FILENAME')
- writer = csv.writer(response, delimiter=app_settings.get('FORM_DESIGNER_CSV_EXPORT_DELIMITER'))
- qs = get_change_list_query_set(FormLogAdmin, FormLog, request)
-
- from django.db.models import Count
- distinct_forms = qs.aggregate(Count('form_definition', distinct=True))['form_definition__count']
-
- include_created = app_settings.get('FORM_DESIGNER_CSV_EXPORT_INCLUDE_CREATED')
- include_pk = app_settings.get('FORM_DESIGNER_CSV_EXPORT_INCLUDE_PK')
- include_header = app_settings.get('FORM_DESIGNER_CSV_EXPORT_INCLUDE_HEADER') and distinct_forms == 1
- include_form = app_settings.get('FORM_DESIGNER_CSV_EXPORT_INCLUDE_FORM') and distinct_forms > 1
-
- if include_header:
- header = []
- if include_form:
- header.append(_('Form'))
- if include_created:
- header.append(_('Created'))
- if include_pk:
- header.append(_('ID'))
- for field in qs.all()[0].data:
- header.append(field['label'] if field['label'] else field['key'])
- writer.writerow(header)
-
- for entry in qs:
- row = []
- if include_form:
- row.append(entry.form_definition)
- if include_created:
- row.append(entry.created)
- if include_pk:
- row.append(entry.pk)
- for field in entry.data:
- value = friendly(field['value'])
- if not isinstance(value, basestring):
- value = unicode(value)
- value = value.encode(settings.DEFAULT_CHARSET)
- row.append(value)
- writer.writerow(row)
-
- return response
diff --git a/form_designer/app_settings.py b/form_designer/app_settings.py
deleted file mode 100644
index 6e91f414..00000000
--- a/form_designer/app_settings.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from django.conf import settings
-from form_designer import defaults
-
-def get(key):
- if hasattr(settings, key):
- return getattr(settings, key)
- else:
- return getattr(defaults, key)
diff --git a/form_designer/cms_plugins.py b/form_designer/cms_plugins.py
index 3486bde1..0012a256 100644
--- a/form_designer/cms_plugins.py
+++ b/form_designer/cms_plugins.py
@@ -1,9 +1,11 @@
+from django.utils.translation import ugettext as _
+
from cms.plugin_base import CMSPluginBase
from cms.plugin_pool import plugin_pool
+
from form_designer.models import CMSFormDefinition
-from django.utils.translation import ugettext as _
-from views import process_form
-from form_designer import app_settings
+from form_designer.views import process_form
+from form_designer import settings
class FormDesignerPlugin(CMSPluginBase):
model = CMSFormDefinition
@@ -14,8 +16,7 @@ def render(self, context, instance, placeholder):
if instance.form_definition.form_template_name:
self.render_template = instance.form_definition.form_template_name
else:
- self.render_template = app_settings.get('FORM_DESIGNER_DEFAULT_FORM_TEMPLATE')
- context.update(process_form(context['request'], instance.form_definition, is_cms_plugin=True))
- return context
+ self.render_template = settings.DEFAULT_FORM_TEMPLATE
+ return process_form(context['request'], instance.form_definition, context, is_cms_plugin=True)
plugin_pool.register_plugin(FormDesignerPlugin)
diff --git a/form_designer/defaults.py b/form_designer/defaults.py
deleted file mode 100644
index 9b164228..00000000
--- a/form_designer/defaults.py
+++ /dev/null
@@ -1,55 +0,0 @@
-from django.utils.translation import ugettext_lazy as _
-
-FORM_DESIGNER_FIELD_CLASSES = (
- ('forms.CharField', _('Text')),
- ('forms.EmailField', _('E-mail address')),
- ('forms.URLField', _('Web address')),
- ('forms.IntegerField', _('Number')),
- ('forms.DecimalField', _('Decimal number')),
- ('forms.BooleanField', _('Yes/No')),
- ('forms.DateField', _('Date')),
- ('forms.DateTimeField', _('Date & time')),
- ('forms.TimeField', _('Time')),
- ('forms.ChoiceField', _('Choice')),
- ('forms.MultipleChoiceField', _('Multiple Choice')),
- ('forms.ModelChoiceField', _('Model Choice')),
- ('forms.ModelMultipleChoiceField', _('Model Multiple Choice')),
- ('forms.RegexField', _('Regex')),
-)
-
-FORM_DESIGNER_WIDGET_CLASSES = (
- ('', _('Default')),
- ('widgets.Textarea', _('Text area')),
- ('widgets.PasswordInput', _('Password input')),
- ('widgets.HiddenInput', _('Hidden input')),
-)
-
-FORM_DESIGNER_FORM_TEMPLATES = (
- ('', _('Default')),
- ('html/formdefinition/forms/as_p.html', _('as paragraphs')),
- ('html/formdefinition/forms/as_table.html', _('as table')),
-)
-
-# Sequence of two-tuples like (('your_app.models.ModelName', 'My Model'), ...) for limiting the models available to ModelChoiceField and ModelMultipleChoiceField.
-# If None, any model can be chosen by entering it as a string
-FORM_DESIGNER_CHOICE_MODEL_CHOICES = None
-
-FORM_DESIGNER_DEFAULT_FORM_TEMPLATE = 'html/formdefinition/forms/default.html'
-
-# semicolon is Microsoft Excel default
-FORM_DESIGNER_CSV_EXPORT_DELIMITER = ';'
-
-# include log timestamp in export
-FORM_DESIGNER_CSV_EXPORT_INCLUDE_CREATED = True
-
-FORM_DESIGNER_CSV_EXPORT_INCLUDE_PK = True
-
-# include field labels/names in first row if exporting logs for one form only
-FORM_DESIGNER_CSV_EXPORT_INCLUDE_HEADER = True
-
-# include form title if exporting logs for more than one form
-FORM_DESIGNER_CSV_EXPORT_INCLUDE_FORM = True
-
-FORM_DESIGNER_CSV_EXPORT_FILENAME = 'export.csv'
-
-FORM_DESIGNER_SUBMIT_FLAG_NAME = 'submit__%s'
\ No newline at end of file
diff --git a/form_designer/model_name_field.py b/form_designer/fields.py
similarity index 51%
rename from form_designer/model_name_field.py
rename to form_designer/fields.py
index 81f32672..e7349772 100644
--- a/form_designer/model_name_field.py
+++ b/form_designer/fields.py
@@ -28,10 +28,42 @@ class ModelNameField(models.CharField):
@staticmethod
def get_model_from_string(model_path):
return ModelNameFormField.get_model_from_string(model_path)
-
+
def formfield(self, **kwargs):
# This is a fairly standard way to set up some defaults
# while letting the caller override them.
defaults = {'form_class': ModelNameFormField}
defaults.update(kwargs)
return super(ModelNameField, self).formfield(**defaults)
+
+class TemplateFormField(forms.CharField):
+
+ def clean(self, value):
+ """
+ Validates that the input can be compiled as a template.
+ """
+ value = super(TemplateFormField, self).clean(value)
+ from django.template import Template, TemplateSyntaxError
+ try:
+ Template(value)
+ except TemplateSyntaxError as error:
+ raise forms.ValidationError(error)
+ return value
+
+class TemplateCharField(models.CharField):
+
+ def formfield(self, **kwargs):
+ # This is a fairly standard way to set up some defaults
+ # while letting the caller override them.
+ defaults = {'form_class': TemplateFormField}
+ defaults.update(kwargs)
+ return super(TemplateCharField, self).formfield(**defaults)
+
+class TemplateTextField(models.TextField):
+
+ def formfield(self, **kwargs):
+ # This is a fairly standard way to set up some defaults
+ # while letting the caller override them.
+ defaults = {'form_class': TemplateFormField}
+ defaults.update(kwargs)
+ return super(TemplateTextField, self).formfield(**defaults)
diff --git a/form_designer/forms.py b/form_designer/forms.py
new file mode 100644
index 00000000..6029032f
--- /dev/null
+++ b/form_designer/forms.py
@@ -0,0 +1,58 @@
+import os
+
+from django import forms
+from django.forms import widgets
+from django.conf import settings as django_settings
+from django.utils.translation import ugettext as _
+
+from form_designer import settings
+from form_designer.models import get_class, FormDefinitionField, FormDefinition
+
+class DesignedForm(forms.Form):
+ def __init__(self, form_definition, initial_data=None, *args, **kwargs):
+ super(DesignedForm, self).__init__(*args, **kwargs)
+ for def_field in form_definition.formdefinitionfield_set.all():
+ self.add_defined_field(def_field, initial_data)
+ self.fields[form_definition.submit_flag_name] = forms.BooleanField(required=False, initial=1, widget=widgets.HiddenInput)
+
+ def add_defined_field(self, def_field, initial_data=None):
+ if initial_data and initial_data.has_key(def_field.name):
+ if not def_field.field_class in ('django.forms.MultipleChoiceField', 'django.forms.ModelMultipleChoiceField'):
+ def_field.initial = initial_data.get(def_field.name)
+ else:
+ def_field.initial = initial_data.getlist(def_field.name)
+ self.fields[def_field.name] = get_class(def_field.field_class)(**def_field.get_form_field_init_args())
+
+
+class FormDefinitionFieldInlineForm(forms.ModelForm):
+ class Meta:
+ model = FormDefinitionField
+
+ def clean_choice_model(self):
+ if not self.cleaned_data['choice_model'] and self.cleaned_data.has_key('field_class') and self.cleaned_data['field_class'] in ('django.forms.ModelChoiceField', 'django.forms.ModelMultipleChoiceField'):
+ raise forms.ValidationError(_('This field class requires a model.'))
+ return self.cleaned_data['choice_model']
+
+
+class FormDefinitionForm(forms.ModelForm):
+ class Meta:
+ model = FormDefinition
+
+ def _media(self):
+ js = []
+ if hasattr(django_settings, 'CMS_MEDIA_URL'):
+ # Use jQuery bundled with django_cms if installed
+ js.append(os.path.join(django_settings.CMS_MEDIA_URL, 'js/lib/jquery.js'))
+ elif hasattr(django_settings, 'JQUERY_URL'):
+ js.append(settings.MEDIA_URL + 'js/jquery.js')
+ js.extend(
+ ['%s%s' % (settings.MEDIA_URL, path) for path in (
+ 'js/jquery-ui.js',
+ 'js/jquery-inline-positioning.js',
+ 'js/jquery-inline-rename.js',
+ 'js/jquery-inline-collapsible.js',
+ 'js/jquery-inline-fieldset-collapsible.js',
+ 'js/jquery-inline-prepopulate-label.js',
+ )])
+ return forms.Media(js=js)
+ media = property(_media)
diff --git a/form_designer/locale/de/LC_MESSAGES/django.mo b/form_designer/locale/de/LC_MESSAGES/django.mo
index 88284681..8d9bf8b0 100644
Binary files a/form_designer/locale/de/LC_MESSAGES/django.mo and b/form_designer/locale/de/LC_MESSAGES/django.mo differ
diff --git a/form_designer/locale/de/LC_MESSAGES/django.po b/form_designer/locale/de/LC_MESSAGES/django.po
index 05968871..e9c97734 100644
--- a/form_designer/locale/de/LC_MESSAGES/django.po
+++ b/form_designer/locale/de/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2010-01-13 14:01+0100\n"
+"POT-Creation-Date: 2010-06-28 14:41+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -16,156 +16,98 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-#: admin.py:15
-msgid "This field class requires a model."
-msgstr "Diese Feld-Klasse benötigt ein Modell."
-
-#: admin.py:23 admin.py:69
+#: admin.py:27 admin.py:38
msgid "Basic"
msgstr "Grundlegendes"
-#: admin.py:24
+#: admin.py:28
msgid "Display"
msgstr "Darstellung"
-#: admin.py:25 defaults.py:4
+#: admin.py:29 settings.py:7
msgid "Text"
msgstr "Text"
-#: admin.py:26
+#: admin.py:30
msgid "Numbers"
msgstr "Zahlen"
-#: admin.py:27 defaults.py:17
+#: admin.py:31 settings.py:20
msgid "Regex"
msgstr "Regex"
-#: admin.py:28
+#: admin.py:32
msgid "Choices"
msgstr "Auswahl"
-#: admin.py:29
+#: admin.py:33
msgid "Model Choices"
msgstr "Modell-Auswahl"
-#: admin.py:70
+#: admin.py:39
msgid "Mail form"
msgstr "Mail-Formular"
-#: admin.py:71
+#: admin.py:40
msgid "Templates"
msgstr "Vorlagen"
-#: admin.py:72
+#: admin.py:41
msgid "Messages"
msgstr "Meldungen"
-#: admin.py:90 admin_views.py:35 cms_plugins.py:10 models.py:31 models.py:107
-#: models.py:243
+#: admin.py:62 admin.py:101 admin.py:146 cms_plugins.py:12 models.py:52
+#: models.py:148 models.py:284
msgid "Form"
msgstr "Formular"
-#: admin.py:95 models.py:108
+#: admin.py:77 models.py:149
msgid "Data"
msgstr "Daten"
-#: admin.py:107
-msgid "CSV export is not enabled."
-msgstr "CSV-Export ist nicht aktiviert."
-
-#: admin_views.py:37 models.py:106
+#: admin.py:103 admin.py:148 models.py:147
msgid "Created"
msgstr "Erstellt"
-#: admin_views.py:39
+#: admin.py:105 admin.py:150
msgid "ID"
msgstr "ID"
-#: defaults.py:5
-msgid "E-mail address"
-msgstr "E-Mail-Adresse"
-
-#: defaults.py:6
-msgid "Web address"
-msgstr "Web-Adresse"
-
-#: defaults.py:7
-msgid "Number"
-msgstr "Zahl"
-
-#: defaults.py:8
-msgid "Decimal number"
-msgstr "Dezimalzahl"
-
-#: defaults.py:9
-msgid "Yes/No"
-msgstr "Ja/Nein"
-
-#: defaults.py:10
-msgid "Date"
-msgstr "Datum"
-
-#: defaults.py:11
-msgid "Date & time"
-msgstr "Datum & Zeit"
-
-#: defaults.py:12
-msgid "Time"
-msgstr "Zeit"
-
-#: defaults.py:13
-msgid "Choice"
-msgstr "Auswahl"
-
-#: defaults.py:14
-msgid "Multiple Choice"
-msgstr "Mehrfach-Auswahl"
-
-#: defaults.py:15
-msgid "Model Choice"
-msgstr "Modell-Auswahl"
-
-#: defaults.py:16
-msgid "Model Multiple Choice"
-msgstr "Modell-Mehrfach-Auswahl"
+#: admin.py:124
+#, python-format
+msgid "Export selected %(verbose_name_plural)s as CSV"
+msgstr "Ausgewählte %(verbose_name_plural)s als CSV exportieren"
-#: defaults.py:21 defaults.py:28
-msgid "Default"
-msgstr "Voreinstellung"
+#: admin.py:172
+#, python-format
+msgid "Export selected %(verbose_name_plural)s as XLS"
+msgstr "Ausgewählte %(verbose_name_plural)s als XLS exportieren"
-#: defaults.py:22
-msgid "Text area"
-msgstr "Textfeld"
-
-#: defaults.py:23
-msgid "Password input"
-msgstr "Passwortfeld"
+#: admin.py:184
+msgid "CSV export is not enabled."
+msgstr "CSV-Export ist nicht aktiviert."
-#: defaults.py:24
-msgid "Hidden input"
-msgstr "Versteckter Wert"
+#: admin.py:189
+msgid "XLS export is not enabled."
+msgstr "XLS-Export ist nicht aktiviert."
-#: defaults.py:29
-msgid "as paragraphs"
-msgstr "als Absätze"
-
-#: defaults.py:30
-msgid "as table"
-msgstr "als Tabelle"
+#: forms.py:33
+msgid "This field class requires a model."
+msgstr "Diese Feld-Klasse benötigt ein Modell."
-#: models.py:13 models.py:121
+#: models.py:34 models.py:162
msgid "Name"
msgstr "Name"
-#: models.py:14
+#: models.py:35
msgid "Title"
msgstr "Titel"
-#: models.py:15
+#: models.py:36
msgid "Target URL"
msgstr "Ziel-URL"
-#: models.py:15
+#: models.py:36
msgid ""
"If you leave this empty, the page where the form resides will be requested, "
"and you can use the mail form and logging features. You can also send data "
@@ -177,188 +119,274 @@ msgstr ""
"werden. Es ist auch möglich, Daten an externe Sites zu senden: Beispielweise "
"\"http://www.google.ch/search\" eingeben, um ein Suchformular zu erstellen."
-#: models.py:16
+#: models.py:37
msgid "Send form data to e-mail address"
msgstr "Formulardaten an E-Mail-Adresse senden"
-#: models.py:17
+#: models.py:38
msgid "Sender address"
msgstr "Absender-Adresse"
-#: models.py:18
+#: models.py:39
msgid "e-Mail subject"
msgstr "E-Mail-Betreff"
-#: models.py:19
+#: models.py:40
msgid "Method"
msgstr "Methode"
-#: models.py:20
+#: models.py:41
msgid "Success message"
msgstr "Erfolgsmeldung"
-#: models.py:21
+#: models.py:42
msgid "Error message"
msgstr "Fehlermeldung"
-#: models.py:22
+#: models.py:43
msgid "Submit button label"
msgstr "Beschriftung der Senden-Schaltfläche"
-#: models.py:23
+#: models.py:44
msgid "Log form data"
msgstr "Formulardaten loggen"
-#: models.py:23
+#: models.py:44
msgid "Logs all form submissions to the database."
msgstr "Speichert alle Formular-Übermittlungen in der Datenbank."
-#: models.py:24
+#: models.py:45
msgid "Redirect after success"
msgstr "Bei Erfolg weiterleiten"
-#: models.py:24
-msgid "You should install django_notify if you want to enable this."
-msgstr "Um dies zu aktivieren, sollte django_notify installiert werden."
-
-#: models.py:25
+#: models.py:46
msgid "Clear form after success"
msgstr "Bei Erfolg Formular zurücksetzen"
-#: models.py:26
+#: models.py:47
msgid "Allow initial values via URL"
msgstr "Initialwerte via URL erlauben"
-#: models.py:26
+#: models.py:47
msgid ""
"If enabled, you can fill in form fields by adding them to the query string."
msgstr ""
"Falls aktiviert, ist es möglich, Formularfelder vorauszufüllen, indem sie "
"dem Query-String hinzugefügt werden."
-#: models.py:27
+#: models.py:48
msgid "Message template"
msgstr "Nachrichten-Vorlage"
-#: models.py:27
+#: models.py:48
+#, fuzzy
msgid ""
-"Available context: \"data\" (a list containing a dictionary for each form "
-"field, each containing the elements \"name\", \"label\", \"value\")."
+"Your form fields are available as template context. Example: \"{{ message }}"
+"\" if you have a field named `message`. To iterate over all fields, use the "
+"variable `data` (a list containing a dictionary for each form field, each "
+"containing the elements `name`, `label`, `value`)."
msgstr ""
+"Verfügbarer Kontext: \"data\" (Eine List mit Dictionaries für jedes Formular "
+"und Feld, jeweils mit den folgenden Elementen: \"name\", \"label\", \"value"
+"\")."
-#: models.py:28
+#: models.py:49
msgid "Form template"
msgstr "Formular-Vorlage"
-#: models.py:32
+#: models.py:53
msgid "Forms"
msgstr "Formulare"
-#: models.py:67 models.py:147 models.py:233
+#: models.py:95 models.py:188 models.py:274
msgid "Fields"
msgstr "Felder"
-#: models.py:111
+#: models.py:152
msgid "Form log"
msgstr "Formular-Log"
-#: models.py:112
+#: models.py:153
msgid "Form logs"
msgstr "Formular-Logs"
-#: models.py:118
+#: models.py:159
msgid "Field class"
msgstr "Feld-Klasse"
-#: models.py:119
+#: models.py:160
msgid "Position"
msgstr "Position"
-#: models.py:122
+#: models.py:163
msgid "Label"
msgstr "Beschriftung"
-#: models.py:123
+#: models.py:164
msgid "Required"
msgstr "Erforderlich"
-#: models.py:124
+#: models.py:165
msgid "Include in result"
msgstr "In Resultat einbeziehen"
-#: models.py:125
+#: models.py:166
msgid "Widget"
msgstr "Widget"
-#: models.py:126
+#: models.py:167
msgid "Initial value"
msgstr "Initialwert"
-#: models.py:127
+#: models.py:168
msgid "Help text"
msgstr "Hilfetext"
-#: models.py:129
+#: models.py:170
msgid "Values"
msgstr "Werte"
-#: models.py:129
+#: models.py:170
msgid "One value per line"
msgstr "Ein Wert pro Zeile"
-#: models.py:130
+#: models.py:171
msgid "Labels"
msgstr "Beschriftungen"
-#: models.py:130
+#: models.py:171
msgid "One label per line"
msgstr "Eine Beschriftung pro Zeile"
-#: models.py:132
+#: models.py:173
msgid "Max. length"
msgstr "Max. Länge"
-#: models.py:133
+#: models.py:174
msgid "Min. length"
msgstr "Min. Länge"
-#: models.py:134
+#: models.py:175
msgid "Max. value"
msgstr "Max. Wert"
-#: models.py:135
+#: models.py:176
msgid "Min. value"
msgstr "Min. Wert"
-#: models.py:136
+#: models.py:177
msgid "Max. digits"
msgstr "Max. Ziffern"
-#: models.py:137
+#: models.py:178
msgid "Decimal places"
msgstr "Dezimalstellen"
-#: models.py:139
+#: models.py:180
msgid "Regular Expression"
msgstr "Regulärer Ausdruck"
-#: models.py:142
+#: models.py:183
msgid "Data model"
msgstr "Datenmodell"
-#: models.py:143
+#: models.py:184
msgid "Empty label"
msgstr "Beschriftung falls leer"
-#: models.py:146 models.py:232
+#: models.py:187 models.py:273
msgid "Field"
msgstr "Feld"
-#: views.py:18
+#: settings.py:8
+msgid "E-mail address"
+msgstr "E-Mail-Adresse"
+
+#: settings.py:9
+msgid "Web address"
+msgstr "Web-Adresse"
+
+#: settings.py:10
+msgid "Number"
+msgstr "Zahl"
+
+#: settings.py:11
+msgid "Decimal number"
+msgstr "Dezimalzahl"
+
+#: settings.py:12
+msgid "Yes/No"
+msgstr "Ja/Nein"
+
+#: settings.py:13
+msgid "Date"
+msgstr "Datum"
+
+#: settings.py:14
+msgid "Date & time"
+msgstr "Datum & Zeit"
+
+#: settings.py:15
+msgid "Time"
+msgstr "Zeit"
+
+#: settings.py:16
+msgid "Choice"
+msgstr "Auswahl"
+
+#: settings.py:17
+msgid "Multiple Choice"
+msgstr "Mehrfach-Auswahl"
+
+#: settings.py:18
+msgid "Model Choice"
+msgstr "Modell-Auswahl"
+
+#: settings.py:19
+msgid "Model Multiple Choice"
+msgstr "Modell-Mehrfach-Auswahl"
+
+#: settings.py:25 settings.py:33
+msgid "Default"
+msgstr "Voreinstellung"
+
+#: settings.py:26
+msgid "Text area"
+msgstr "Textfeld"
+
+#: settings.py:27
+msgid "Password input"
+msgstr "Passwortfeld"
+
+#: settings.py:28
+msgid "Hidden input"
+msgstr "Versteckter Wert"
+
+#: settings.py:29
+msgid "Radio button"
+msgstr "Radio-Buttons"
+
+#: settings.py:34
+msgid "as paragraphs"
+msgstr "als Absätze"
+
+#: settings.py:35
+msgid "as table"
+msgstr "als Tabelle"
+
+#: settings.py:36
+msgid "as unordered list"
+msgstr "als unsortierte Liste"
+
+#: settings.py:37
+msgid "custom implementation"
+msgstr "eigene Implementierung"
+
+#: views.py:13
msgid "Thank you, the data was submitted successfully."
msgstr "Vielen Dank, die Daten wurden übermittelt."
-#: views.py:19
+#: views.py:14
msgid "The data could not be submitted, please try again."
msgstr ""
"Die Daten konnten nicht übermittelt werden, bitte versuchen Sie es erneut."
@@ -367,10 +395,22 @@ msgstr ""
msgid "Export CSV"
msgstr "CSV exportieren"
-#: templates/html/formdefinition/forms/as_p.html:10
-#: templates/html/formdefinition/forms/as_table.html:10
+#: templates/admin/form_designer/formlog/change_list.html:16
+#, fuzzy
+msgid "Export XLS"
+msgstr "XLS exportieren"
+
+#: templates/html/formdefinition/forms/as_p.html:11
+#: templates/html/formdefinition/forms/as_table.html:11
+#: templates/html/formdefinition/forms/as_u.html:11
+#: templates/html/formdefinition/forms/custom.html:40
msgid "Submit"
msgstr "Absenden"
-#~ msgid "Captcha"
-#~ msgstr "Captcha"
+#: templatetags/friendly.py:16
+msgid "yes"
+msgstr "ja"
+
+#: templatetags/friendly.py:16
+msgid "no"
+msgstr "nein"
diff --git a/form_designer/locale/en/LC_MESSAGES/django.po b/form_designer/locale/en/LC_MESSAGES/django.po
new file mode 100644
index 00000000..b5b625d0
--- /dev/null
+++ b/form_designer/locale/en/LC_MESSAGES/django.po
@@ -0,0 +1,404 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR , YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-06-28 14:41+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: admin.py:27 admin.py:38
+msgid "Basic"
+msgstr ""
+
+#: admin.py:28
+msgid "Display"
+msgstr ""
+
+#: admin.py:29 settings.py:7
+msgid "Text"
+msgstr ""
+
+#: admin.py:30
+msgid "Numbers"
+msgstr ""
+
+#: admin.py:31 settings.py:20
+msgid "Regex"
+msgstr ""
+
+#: admin.py:32
+msgid "Choices"
+msgstr ""
+
+#: admin.py:33
+msgid "Model Choices"
+msgstr ""
+
+#: admin.py:39
+msgid "Mail form"
+msgstr ""
+
+#: admin.py:40
+msgid "Templates"
+msgstr ""
+
+#: admin.py:41
+msgid "Messages"
+msgstr ""
+
+#: admin.py:62 admin.py:101 admin.py:146 cms_plugins.py:12 models.py:52
+#: models.py:148 models.py:284
+msgid "Form"
+msgstr ""
+
+#: admin.py:77 models.py:149
+msgid "Data"
+msgstr ""
+
+#: admin.py:103 admin.py:148 models.py:147
+msgid "Created"
+msgstr ""
+
+#: admin.py:105 admin.py:150
+msgid "ID"
+msgstr ""
+
+#: admin.py:124
+#, python-format
+msgid "Export selected %(verbose_name_plural)s as CSV"
+msgstr ""
+
+#: admin.py:172
+#, python-format
+msgid "Export selected %(verbose_name_plural)s as XLS"
+msgstr ""
+
+#: admin.py:184
+msgid "CSV export is not enabled."
+msgstr ""
+
+#: admin.py:189
+msgid "XLS export is not enabled."
+msgstr ""
+
+#: forms.py:33
+msgid "This field class requires a model."
+msgstr ""
+
+#: models.py:34 models.py:162
+msgid "Name"
+msgstr ""
+
+#: models.py:35
+msgid "Title"
+msgstr ""
+
+#: models.py:36
+msgid "Target URL"
+msgstr ""
+
+#: models.py:36
+msgid ""
+"If you leave this empty, the page where the form resides will be requested, "
+"and you can use the mail form and logging features. You can also send data "
+"to external sites: For instance, enter \"http://www.google.ch/search\" to "
+"create a search form."
+msgstr ""
+
+#: models.py:37
+msgid "Send form data to e-mail address"
+msgstr ""
+
+#: models.py:38
+msgid "Sender address"
+msgstr ""
+
+#: models.py:39
+msgid "e-Mail subject"
+msgstr ""
+
+#: models.py:40
+msgid "Method"
+msgstr ""
+
+#: models.py:41
+msgid "Success message"
+msgstr ""
+
+#: models.py:42
+msgid "Error message"
+msgstr ""
+
+#: models.py:43
+msgid "Submit button label"
+msgstr ""
+
+#: models.py:44
+msgid "Log form data"
+msgstr ""
+
+#: models.py:44
+msgid "Logs all form submissions to the database."
+msgstr ""
+
+#: models.py:45
+msgid "Redirect after success"
+msgstr ""
+
+#: models.py:46
+msgid "Clear form after success"
+msgstr ""
+
+#: models.py:47
+msgid "Allow initial values via URL"
+msgstr ""
+
+#: models.py:47
+msgid ""
+"If enabled, you can fill in form fields by adding them to the query string."
+msgstr ""
+
+#: models.py:48
+msgid "Message template"
+msgstr ""
+
+#: models.py:48
+msgid ""
+"Your form fields are available as template context. Example: \"{{ message }}"
+"\" if you have a field named `message`. To iterate over all fields, use the "
+"variable `data` (a list containing a dictionary for each form field, each "
+"containing the elements `name`, `label`, `value`)."
+msgstr ""
+
+#: models.py:49
+msgid "Form template"
+msgstr ""
+
+#: models.py:53
+msgid "Forms"
+msgstr ""
+
+#: models.py:95 models.py:188 models.py:274
+msgid "Fields"
+msgstr ""
+
+#: models.py:152
+msgid "Form log"
+msgstr ""
+
+#: models.py:153
+msgid "Form logs"
+msgstr ""
+
+#: models.py:159
+msgid "Field class"
+msgstr ""
+
+#: models.py:160
+msgid "Position"
+msgstr ""
+
+#: models.py:163
+msgid "Label"
+msgstr ""
+
+#: models.py:164
+msgid "Required"
+msgstr ""
+
+#: models.py:165
+msgid "Include in result"
+msgstr ""
+
+#: models.py:166
+msgid "Widget"
+msgstr ""
+
+#: models.py:167
+msgid "Initial value"
+msgstr ""
+
+#: models.py:168
+msgid "Help text"
+msgstr ""
+
+#: models.py:170
+msgid "Values"
+msgstr ""
+
+#: models.py:170
+msgid "One value per line"
+msgstr ""
+
+#: models.py:171
+msgid "Labels"
+msgstr ""
+
+#: models.py:171
+msgid "One label per line"
+msgstr ""
+
+#: models.py:173
+msgid "Max. length"
+msgstr ""
+
+#: models.py:174
+msgid "Min. length"
+msgstr ""
+
+#: models.py:175
+msgid "Max. value"
+msgstr ""
+
+#: models.py:176
+msgid "Min. value"
+msgstr ""
+
+#: models.py:177
+msgid "Max. digits"
+msgstr ""
+
+#: models.py:178
+msgid "Decimal places"
+msgstr ""
+
+#: models.py:180
+msgid "Regular Expression"
+msgstr ""
+
+#: models.py:183
+msgid "Data model"
+msgstr ""
+
+#: models.py:184
+msgid "Empty label"
+msgstr ""
+
+#: models.py:187 models.py:273
+msgid "Field"
+msgstr ""
+
+#: settings.py:8
+msgid "E-mail address"
+msgstr ""
+
+#: settings.py:9
+msgid "Web address"
+msgstr ""
+
+#: settings.py:10
+msgid "Number"
+msgstr ""
+
+#: settings.py:11
+msgid "Decimal number"
+msgstr ""
+
+#: settings.py:12
+msgid "Yes/No"
+msgstr ""
+
+#: settings.py:13
+msgid "Date"
+msgstr ""
+
+#: settings.py:14
+msgid "Date & time"
+msgstr ""
+
+#: settings.py:15
+msgid "Time"
+msgstr ""
+
+#: settings.py:16
+msgid "Choice"
+msgstr ""
+
+#: settings.py:17
+msgid "Multiple Choice"
+msgstr ""
+
+#: settings.py:18
+msgid "Model Choice"
+msgstr ""
+
+#: settings.py:19
+msgid "Model Multiple Choice"
+msgstr ""
+
+#: settings.py:25 settings.py:33
+msgid "Default"
+msgstr ""
+
+#: settings.py:26
+msgid "Text area"
+msgstr ""
+
+#: settings.py:27
+msgid "Password input"
+msgstr ""
+
+#: settings.py:28
+msgid "Hidden input"
+msgstr ""
+
+#: settings.py:29
+msgid "Radio button"
+msgstr ""
+
+#: settings.py:34
+msgid "as paragraphs"
+msgstr ""
+
+#: settings.py:35
+msgid "as table"
+msgstr ""
+
+#: settings.py:36
+msgid "as unordered list"
+msgstr ""
+
+#: settings.py:37
+msgid "custom implementation"
+msgstr ""
+
+#: views.py:13
+msgid "Thank you, the data was submitted successfully."
+msgstr ""
+
+#: views.py:14
+msgid "The data could not be submitted, please try again."
+msgstr ""
+
+#: templates/admin/form_designer/formlog/change_list.html:9
+msgid "Export CSV"
+msgstr ""
+
+#: templates/admin/form_designer/formlog/change_list.html:16
+msgid "Export XLS"
+msgstr ""
+
+#: templates/html/formdefinition/forms/as_p.html:11
+#: templates/html/formdefinition/forms/as_table.html:11
+#: templates/html/formdefinition/forms/as_u.html:11
+#: templates/html/formdefinition/forms/custom.html:40
+msgid "Submit"
+msgstr ""
+
+#: templatetags/friendly.py:16
+msgid "yes"
+msgstr ""
+
+#: templatetags/friendly.py:16
+msgid "no"
+msgstr ""
diff --git a/form_designer/locale/nl/LC_MESSAGES/django.mo b/form_designer/locale/nl/LC_MESSAGES/django.mo
new file mode 100644
index 00000000..f6aea2ea
Binary files /dev/null and b/form_designer/locale/nl/LC_MESSAGES/django.mo differ
diff --git a/form_designer/locale/nl/LC_MESSAGES/django.po b/form_designer/locale/nl/LC_MESSAGES/django.po
new file mode 100644
index 00000000..a9a5d26a
--- /dev/null
+++ b/form_designer/locale/nl/LC_MESSAGES/django.po
@@ -0,0 +1,309 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-06-28 14:41+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Erik Romijn \n"
+"Language-Team: LANGUAGE \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "Allow initial values via URL"
+msgstr "Initiële waarden via de URL toelaten"
+
+msgid "Basic"
+msgstr "Basis"
+
+msgid "CSV export is not enabled."
+msgstr "CSV-exporteren is niet geäctiveerd."
+
+msgid "Choice"
+msgstr "Keuze"
+
+msgid "Choices"
+msgstr "Keuzes"
+
+msgid "Clear form after success"
+msgstr "Na succesvol versturen formulier leegmaken"
+
+msgid "Created"
+msgstr "Aangemaakt"
+
+msgid "Data"
+msgstr "Data"
+
+msgid "Data model"
+msgstr "Datamodel"
+
+msgid "Date"
+msgstr "Datum"
+
+msgid "Date & time"
+msgstr "Datum & tijd"
+
+msgid "Decimal number"
+msgstr "Decimaal nummer"
+
+msgid "Decimal places"
+msgstr "Decimalen"
+
+msgid "Default"
+msgstr "Standaard"
+
+msgid "Display"
+msgstr "Weergave"
+
+msgid "E-mail address"
+msgstr "E-mail-adres"
+
+msgid "Empty label"
+msgstr "Leeg label"
+
+msgid "Error message"
+msgstr "Foutmelding"
+
+msgid "Export CSV"
+msgstr "CSV exporteren"
+
+msgid "Export XLS"
+msgstr "XLS exporteren"
+
+msgid "Export selected %(verbose_name_plural)s as CSV"
+msgstr "Exporteer geselecteerde %(verbose_name_plural) als CSV"
+
+msgid "Export selected %(verbose_name_plural)s as XLS"
+msgstr "Exporteer geselecteerde %(verbose_name_plural) als Excel XLS"
+
+msgid "Field"
+msgstr "Veld"
+
+msgid "Field class"
+msgstr "Veldklasse"
+
+msgid "Fields"
+msgstr "Velden"
+
+msgid "Form"
+msgstr "Formulier"
+
+msgid "Form log"
+msgstr "Formulier-log"
+
+msgid "Form logs"
+msgstr "Formulier-logs"
+
+msgid "Form template"
+msgstr "Formulier-sjabloon"
+
+msgid "Forms"
+msgstr "Formulieren"
+
+msgid "Help text"
+msgstr "Hulptekst"
+
+msgid "Hidden input"
+msgstr "Verborgen veld"
+
+msgid "ID"
+msgstr "ID"
+
+msgid ""
+"If enabled, you can fill in form fields by adding them to the query string."
+msgstr ""
+"Indien geactiveerd, kunt u formuliervelden invullen door deze aan de query-"
+"string toe te voegen. "
+
+msgid ""
+"If you leave this empty, the page where the form resides will be requested, "
+"and you can use the mail form and logging features. You can also send data "
+"to external sites: For instance, enter \"http://www.google.ch/search\" to "
+"create a search form."
+msgstr ""
+"Als u dit leeg laat, wordt de pagina waar het formulier zich bevindt "
+"opgevraagd en kan u de e-mail en log-functies gebruiken. U kunt ook data "
+"naar externe sites versturen. Bijvoorbeeld, voer \"http://www.google.com/"
+"search\" in om een zoekformulier aan te maken. "
+
+msgid "Include in result"
+msgstr "In resultaat plaatsen"
+
+msgid "Initial value"
+msgstr "Initiële waarde"
+
+msgid "Label"
+msgstr "Label"
+
+msgid "Labels"
+msgstr "Labels"
+
+msgid "Log form data"
+msgstr "Formulierdata loggen"
+
+msgid "Logs all form submissions to the database."
+msgstr "Log alle formulierinsturingen in de database."
+
+msgid "Mail form"
+msgstr "Mail-formulier"
+
+msgid "Max. digits"
+msgstr "Max. aantal cijfers"
+
+msgid "Max. length"
+msgstr "Max. lengte"
+
+msgid "Max. value"
+msgstr "Max. waarde"
+
+msgid "Message template"
+msgstr "Berichtsjabloon"
+
+msgid "Messages"
+msgstr "Berichten"
+
+msgid "Method"
+msgstr "Methode"
+
+msgid "Min. length"
+msgstr "Min. lengte"
+
+msgid "Min. value"
+msgstr "Min. waarde"
+
+msgid "Model Choice"
+msgstr "Model-keuze"
+
+msgid "Model Choices"
+msgstr "Model-keuzes"
+
+msgid "Model Multiple Choice"
+msgstr "Model-meerkeuze"
+
+msgid "Multiple Choice"
+msgstr "Meerkeuze"
+
+msgid "Name"
+msgstr "Naam"
+
+msgid "Number"
+msgstr "Nummer"
+
+msgid "Numbers"
+msgstr "Nummers"
+
+msgid "One label per line"
+msgstr "Eén label per regel"
+
+msgid "One value per line"
+msgstr "Eén waarde per regel"
+
+msgid "Password input"
+msgstr "Wachtwoordveld"
+
+msgid "Position"
+msgstr "Positie"
+
+msgid "Radio button"
+msgstr "Keuzerondje"
+
+msgid "Redirect after success"
+msgstr "Doorsturen na success"
+
+msgid "Regex"
+msgstr "Reguliere expressie"
+
+msgid "Regular Expression"
+msgstr "Reguliere expressie"
+
+msgid "Required"
+msgstr "Vereist"
+
+msgid "Send form data to e-mail address"
+msgstr "Formuliergegevens naar e-mail-adres sturen"
+
+msgid "Sender address"
+msgstr "Afzender-adres"
+
+msgid "Submit"
+msgstr "Verzenden"
+
+msgid "Submit button label"
+msgstr "Label voor de verzendknop"
+
+msgid "Success message"
+msgstr "Successmelding"
+
+msgid "Target URL"
+msgstr "Doel-URL"
+
+msgid "Templates"
+msgstr "Sjablonen"
+
+msgid "Text"
+msgstr "Tekst"
+
+msgid "Text area"
+msgstr "Tekstveld"
+
+msgid "Thank you, the data was submitted successfully."
+msgstr "Hartelijk dank. Uw gegevens zijn doorgestuurd."
+
+msgid "The data could not be submitted, please try again."
+msgstr "De gegevens konden niet opgestuurd worden, probeert u het opnieuw."
+
+msgid "This field class requires a model."
+msgstr "Deze veldklasse vereist een model."
+
+msgid "Time"
+msgstr "Tijd"
+
+msgid "Title"
+msgstr "Titel"
+
+msgid "Values"
+msgstr "Waardes"
+
+msgid "Web address"
+msgstr "Web-adres"
+
+msgid "Widget"
+msgstr "Widget"
+
+msgid "XLS export is not enabled."
+msgstr "XLS-exporteren is niet geäctiveerd."
+
+msgid "Yes/No"
+msgstr "Ja/Nee"
+
+msgid ""
+"Your form fields are available as template context. Example: \"{{ message }}"
+"\" if you have a field named `message`. To iterate over all fields, use the "
+"variable `data` (a list containing a dictionary for each form field, each "
+"containing the elements `name`, `label`, `value`)."
+msgstr ""
+"Uw formuliervelden zijn beschikbaar als template context. Bijvoorbeeld: "
+"\"{{ bericht }}\" als u een veld genaamd `bericht` heeft. Om over alle "
+"velden te itereren, gebruikt u de variabele `data`: een lijst met een dict "
+"voor elk veld met de elementen `name`, `label` en `value`."
+
+msgid "as paragraphs"
+msgstr "als paragraaf"
+
+msgid "as table"
+msgstr "als tabel"
+
+msgid "as unordered list"
+msgstr "als ongesorteerde lijst"
+
+msgid "custom implementation"
+msgstr "eigen implementatie"
+
+msgid "e-Mail subject"
+msgstr "E-mail-onderwerp"
+
+msgid "no"
+msgstr "nee"
+
+msgid "yes"
+msgstr "ja"
diff --git a/form_designer/media/form_designer/js/jquery-inline-collapsible.js b/form_designer/media/form_designer/js/jquery-inline-collapsible.js
new file mode 100644
index 00000000..b1f863a2
--- /dev/null
+++ b/form_designer/media/form_designer/js/jquery-inline-collapsible.js
@@ -0,0 +1,50 @@
+function makeCollapsible(target, item, collapsible, triggerTarget, setInitStatus, setFirstStatus)
+{
+ var triggerExpand = gettext('Show');
+ var triggerCollapse = gettext('Hide');
+ var triggerLink = '';
+ var triggerPrepend = ' (';
+ var triggerAppend = ')';
+
+ $(target).find(item).each(function(i) {
+ $(this).find(collapsible).hide();
+
+ var trigger = $(triggerLink)
+ $(this).find(triggerTarget).append(trigger);
+ trigger.before(triggerPrepend);
+ trigger.after(triggerAppend);
+ 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).show();
+ } else {
+ trigger.html(triggerExpand);
+ $(item).find(collapsible).hide();
+ }
+ }
+
+ 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)
+ });
+}
+
+jQuery(function($) {
+ makeCollapsible('div.inline-group', 'div.inline-related', 'fieldset', 'h3 b');
+});
diff --git a/form_designer/media/form_designer/js/jquery-inline-fieldset-collapsible.js b/form_designer/media/form_designer/js/jquery-inline-fieldset-collapsible.js
new file mode 100644
index 00000000..c4b02044
--- /dev/null
+++ b/form_designer/media/form_designer/js/jquery-inline-fieldset-collapsible.js
@@ -0,0 +1,5 @@
+jQuery(function($) {
+ $('div.inline-related').each(function(i) {
+ makeCollapsible(this, 'fieldset', '.form-row', 'h2', null, true)
+ });
+});
diff --git a/form_designer/media/form_designer/js/jquery-inline-positioning.js b/form_designer/media/form_designer/js/jquery-inline-positioning.js
new file mode 100644
index 00000000..295a8a74
--- /dev/null
+++ b/form_designer/media/form_designer/js/jquery-inline-positioning.js
@@ -0,0 +1,45 @@
+/*
+Enables positioning of the inline elements by drag & drop.
+
+All 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 can then be ordered by "position".
+*/
+
+jQuery(function($) {
+
+ var positionField = $.scriptUrlParam ? $.scriptUrlParam(/jquery-inline-positioning\.js(\?.*)?$/, 'positionField', 'position') : 'position';
+ var target = $('div.inline-group');
+ var handle = 'h3 b';
+ var item = 'div.inline-related';
+ var positionInput = 'input[id$=-'+positionField+']';
+
+ target.find(item).each(function(i) {
+ $(this).find(handle).css('cursor', 'move');
+ $(this).find(handle).addClass('draggable');
+ $(this).find(positionInput).each(function() {
+ $(this)[0].readOnly = true;
+ });
+ $(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 renumberAll = function() {
+ target.find(item).each(function(i) {
+ if ($(this).find(positionInput).val() != '') {
+ $(this).find(positionInput).val(i+1);
+ }
+ });
+ };
+
+ target.sortable({
+ containment: 'parent',
+ /*zindex: 10, */
+ items: item,
+ handle: handle,
+ update: renumberAll,
+ opacity: .75
+ });
+});
diff --git a/form_designer/media/form_designer/js/jquery-inline-prepopulate-label.js b/form_designer/media/form_designer/js/jquery-inline-prepopulate-label.js
new file mode 100644
index 00000000..ad9cba5a
--- /dev/null
+++ b/form_designer/media/form_designer/js/jquery-inline-prepopulate-label.js
@@ -0,0 +1,29 @@
+/*
+Does exactly what
+ prepopulated_fields = {"label" : ('name',)}
+would do, but does not URLify the value (since in this case, name is a slug field but label is a title field)
+*/
+
+
+jQuery(function($) {
+
+ var target = $('div.inline-group');
+ var item = 'div.inline-related';
+
+ $(target).find(item).each(function(i) {
+ var item = $(this);
+ item.find('input[id*=-label]').each(function() {
+ this._changed = item.find('input[id*=-name]').val() != $(this).val();
+ });
+ item.find('input[id*=-label]').change(function() {
+ this._changed = true;
+ });
+ item.find('input[id*=-name]').keyup(function() {
+ labelInput = item.find('input[id*=-label]');
+ if (!labelInput[0]._changed) {
+ labelInput.val($(this).val());
+ }
+ });
+ });
+
+});
\ No newline at end of file
diff --git a/form_designer/media/form_designer/js/jquery-inline-rename.js b/form_designer/media/form_designer/js/jquery-inline-rename.js
new file mode 100644
index 00000000..3c55b14e
--- /dev/null
+++ b/form_designer/media/form_designer/js/jquery-inline-rename.js
@@ -0,0 +1,44 @@
+/*
+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 nameField = $.scriptUrlParam ? $.scriptUrlParam(/jquery-inline-rename\.js(\?.*)?$/, 'nameField', 'name') : 'name';
+ var target = $('div.inline-group');
+ var item = 'div.inline-related';
+ var containsName = 'h3';
+ var beforeName = 'h3 > *:first';
+ var afterName = 'h3 > *:last';
+ var nameInput = 'input[id*=-'+nameField+']';
+
+ target.find(item).each(function() {
+ // The following code is due to the fact that the inline
+ // element's name is (lamentably) not wrapped in a tag.
+
+ // 1. Strip everything before and after name
+ var stripBefore = $(this).find(beforeName).remove();
+ var stripAfter = $(this).find(afterName).remove();
+ // 2. Now we can get the name (text node)
+ var nameParent = $(this).find(containsName);
+ var name = $(nameParent).html();
+ // 3. Strip leading whitespace including
+ name = name.replace( /^(\s| )+/g, "" )
+ // 4. Wrap name in tag
+ name = $(''+name+'');
+ // 5. Re-insert it with a space before
+ $(nameParent).html(name);
+ $(name).before(' ');
+ // 6. Restore everything before and after name
+ $(nameParent).prepend(stripBefore);
+ $(nameParent).append(stripAfter);
+
+ // Update name while typing
+ $(this).find(nameInput).keyup(function(event) {
+ name.html($(this).val());
+ })
+ })
+
+
+});
diff --git a/form_designer/media/form_designer/js/lib/jquery-ui.js b/form_designer/media/form_designer/js/jquery-ui.js
similarity index 100%
rename from form_designer/media/form_designer/js/lib/jquery-ui.js
rename to form_designer/media/form_designer/js/jquery-ui.js
diff --git a/form_designer/media/form_designer/js/jquery-url-param.js b/form_designer/media/form_designer/js/jquery-url-param.js
new file mode 100644
index 00000000..7572a82b
--- /dev/null
+++ b/form_designer/media/form_designer/js/jquery-url-param.js
@@ -0,0 +1,17 @@
+$.urlParam = function(name, defaultValue, url) {
+ if (!url) {
+ url = window.location.href
+ }
+ var results = new RegExp('[\\?&]'+name+'=([^]*)').exec(url);
+ return results ? results[1] : defaultValue;
+}
+
+$.scriptUrlParam = function(js, name, defaultValue) {
+ result = defaultValue;
+ $('head script[src]').each(function() {
+ if (this.src.match(js)) {
+ result = $.urlParam(name, result, this.src);
+ }
+ });
+ return result;
+}
diff --git a/form_designer/media/form_designer/js/lib/jquery.js b/form_designer/media/form_designer/js/jquery.js
similarity index 100%
rename from form_designer/media/form_designer/js/lib/jquery.js
rename to form_designer/media/form_designer/js/jquery.js
diff --git a/form_designer/media/form_designer/js/lib/django-admin-tweaks-js-lib b/form_designer/media/form_designer/js/lib/django-admin-tweaks-js-lib
deleted file mode 160000
index 6fddb31c..00000000
--- a/form_designer/media/form_designer/js/lib/django-admin-tweaks-js-lib
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 6fddb31c46f1bd4a153d4814cf5c4c62884602cc
diff --git a/form_designer/media/form_designer/js/lib/django-admin-tweaks-js-lib/README.md b/form_designer/media/form_designer/js/lib/django-admin-tweaks-js-lib/README.md
new file mode 100644
index 00000000..5f8129fc
--- /dev/null
+++ b/form_designer/media/form_designer/js/lib/django-admin-tweaks-js-lib/README.md
@@ -0,0 +1,4 @@
+django-admin-tweaks-js-lib
+==========================
+
+Javascript Library for the Django admin_tweaks app, for internal use._
diff --git a/form_designer/media/form_designer/js/lib/django-admin-tweaks-js-lib/js/jquery-inline-collapsible.js b/form_designer/media/form_designer/js/lib/django-admin-tweaks-js-lib/js/jquery-inline-collapsible.js
new file mode 100644
index 00000000..12d4fcf9
--- /dev/null
+++ b/form_designer/media/form_designer/js/lib/django-admin-tweaks-js-lib/js/jquery-inline-collapsible.js
@@ -0,0 +1,50 @@
+function makeCollapsible(target, item, collapsible, triggerTarget, setInitStatus, setFirstStatus)
+{
+ var triggerExpand = gettext('Show');
+ var triggerCollapse = gettext('Hide');
+ var triggerLink = '';
+ var triggerPrepend = ' ( ';
+ var triggerAppend = ' )';
+
+ $(target).find(item).each(function(i) {
+ $(this).find(collapsible).slideUp(1);
+
+ var trigger = $(triggerLink)
+ $(this).find(triggerTarget).append(trigger);
+ trigger.before(triggerPrepend);
+ trigger.after(triggerAppend);
+ 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(speed);
+ } else {
+ trigger.html(triggerExpand);
+ $(item).find(collapsible).slideUp(speed);
+ }
+ }
+
+ 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)
+ });
+}
+
+jQuery(function($) {
+ makeCollapsible('div.inline-group', 'div.inline-related', 'fieldset', 'h3 b');
+});
diff --git a/form_designer/media/form_designer/js/lib/django-admin-tweaks-js-lib/js/jquery-inline-fieldset-collapsible.js b/form_designer/media/form_designer/js/lib/django-admin-tweaks-js-lib/js/jquery-inline-fieldset-collapsible.js
new file mode 100644
index 00000000..c4b02044
--- /dev/null
+++ b/form_designer/media/form_designer/js/lib/django-admin-tweaks-js-lib/js/jquery-inline-fieldset-collapsible.js
@@ -0,0 +1,5 @@
+jQuery(function($) {
+ $('div.inline-related').each(function(i) {
+ makeCollapsible(this, 'fieldset', '.form-row', 'h2', null, true)
+ });
+});
diff --git a/form_designer/media/form_designer/js/lib/django-admin-tweaks-js-lib/js/jquery-inline-positioning.js b/form_designer/media/form_designer/js/lib/django-admin-tweaks-js-lib/js/jquery-inline-positioning.js
new file mode 100644
index 00000000..295a8a74
--- /dev/null
+++ b/form_designer/media/form_designer/js/lib/django-admin-tweaks-js-lib/js/jquery-inline-positioning.js
@@ -0,0 +1,45 @@
+/*
+Enables positioning of the inline elements by drag & drop.
+
+All 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 can then be ordered by "position".
+*/
+
+jQuery(function($) {
+
+ var positionField = $.scriptUrlParam ? $.scriptUrlParam(/jquery-inline-positioning\.js(\?.*)?$/, 'positionField', 'position') : 'position';
+ var target = $('div.inline-group');
+ var handle = 'h3 b';
+ var item = 'div.inline-related';
+ var positionInput = 'input[id$=-'+positionField+']';
+
+ target.find(item).each(function(i) {
+ $(this).find(handle).css('cursor', 'move');
+ $(this).find(handle).addClass('draggable');
+ $(this).find(positionInput).each(function() {
+ $(this)[0].readOnly = true;
+ });
+ $(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 renumberAll = function() {
+ target.find(item).each(function(i) {
+ if ($(this).find(positionInput).val() != '') {
+ $(this).find(positionInput).val(i+1);
+ }
+ });
+ };
+
+ target.sortable({
+ containment: 'parent',
+ /*zindex: 10, */
+ items: item,
+ handle: handle,
+ update: renumberAll,
+ opacity: .75
+ });
+});
diff --git a/form_designer/media/form_designer/js/lib/django-admin-tweaks-js-lib/js/jquery-inline-prepopulate-label.js b/form_designer/media/form_designer/js/lib/django-admin-tweaks-js-lib/js/jquery-inline-prepopulate-label.js
new file mode 100644
index 00000000..ad9cba5a
--- /dev/null
+++ b/form_designer/media/form_designer/js/lib/django-admin-tweaks-js-lib/js/jquery-inline-prepopulate-label.js
@@ -0,0 +1,29 @@
+/*
+Does exactly what
+ prepopulated_fields = {"label" : ('name',)}
+would do, but does not URLify the value (since in this case, name is a slug field but label is a title field)
+*/
+
+
+jQuery(function($) {
+
+ var target = $('div.inline-group');
+ var item = 'div.inline-related';
+
+ $(target).find(item).each(function(i) {
+ var item = $(this);
+ item.find('input[id*=-label]').each(function() {
+ this._changed = item.find('input[id*=-name]').val() != $(this).val();
+ });
+ item.find('input[id*=-label]').change(function() {
+ this._changed = true;
+ });
+ item.find('input[id*=-name]').keyup(function() {
+ labelInput = item.find('input[id*=-label]');
+ if (!labelInput[0]._changed) {
+ labelInput.val($(this).val());
+ }
+ });
+ });
+
+});
\ No newline at end of file
diff --git a/form_designer/media/form_designer/js/lib/django-admin-tweaks-js-lib/js/jquery-inline-rename.js b/form_designer/media/form_designer/js/lib/django-admin-tweaks-js-lib/js/jquery-inline-rename.js
new file mode 100644
index 00000000..3c55b14e
--- /dev/null
+++ b/form_designer/media/form_designer/js/lib/django-admin-tweaks-js-lib/js/jquery-inline-rename.js
@@ -0,0 +1,44 @@
+/*
+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 nameField = $.scriptUrlParam ? $.scriptUrlParam(/jquery-inline-rename\.js(\?.*)?$/, 'nameField', 'name') : 'name';
+ var target = $('div.inline-group');
+ var item = 'div.inline-related';
+ var containsName = 'h3';
+ var beforeName = 'h3 > *:first';
+ var afterName = 'h3 > *:last';
+ var nameInput = 'input[id*=-'+nameField+']';
+
+ target.find(item).each(function() {
+ // The following code is due to the fact that the inline
+ // element's name is (lamentably) not wrapped in a tag.
+
+ // 1. Strip everything before and after name
+ var stripBefore = $(this).find(beforeName).remove();
+ var stripAfter = $(this).find(afterName).remove();
+ // 2. Now we can get the name (text node)
+ var nameParent = $(this).find(containsName);
+ var name = $(nameParent).html();
+ // 3. Strip leading whitespace including
+ name = name.replace( /^(\s| )+/g, "" )
+ // 4. Wrap name in tag
+ name = $(''+name+'');
+ // 5. Re-insert it with a space before
+ $(nameParent).html(name);
+ $(name).before(' ');
+ // 6. Restore everything before and after name
+ $(nameParent).prepend(stripBefore);
+ $(nameParent).append(stripAfter);
+
+ // Update name while typing
+ $(this).find(nameInput).keyup(function(event) {
+ name.html($(this).val());
+ })
+ })
+
+
+});
diff --git a/form_designer/media/form_designer/js/lib/django-admin-tweaks-js-lib/js/jquery-url-param.js b/form_designer/media/form_designer/js/lib/django-admin-tweaks-js-lib/js/jquery-url-param.js
new file mode 100644
index 00000000..7572a82b
--- /dev/null
+++ b/form_designer/media/form_designer/js/lib/django-admin-tweaks-js-lib/js/jquery-url-param.js
@@ -0,0 +1,17 @@
+$.urlParam = function(name, defaultValue, url) {
+ if (!url) {
+ url = window.location.href
+ }
+ var results = new RegExp('[\\?&]'+name+'=([^]*)').exec(url);
+ return results ? results[1] : defaultValue;
+}
+
+$.scriptUrlParam = function(js, name, defaultValue) {
+ result = defaultValue;
+ $('head script[src]').each(function() {
+ if (this.src.match(js)) {
+ result = $.urlParam(name, result, this.src);
+ }
+ });
+ return result;
+}
diff --git a/form_designer/migrations/0001_initial.py b/form_designer/migrations/0001_initial.py
new file mode 100644
index 00000000..59d6b3c6
--- /dev/null
+++ b/form_designer/migrations/0001_initial.py
@@ -0,0 +1,175 @@
+# encoding: utf-8
+import 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 'FormDefinition'
+ db.create_table('form_designer_formdefinition', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('name', self.gf('django.db.models.fields.SlugField')(unique=True, max_length=255, db_index=True)),
+ ('title', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+ ('action', self.gf('django.db.models.fields.URLField')(max_length=255, null=True, blank=True)),
+ ('mail_to', self.gf('form_designer.fields.TemplateCharField')(max_length=255, null=True, blank=True)),
+ ('mail_from', self.gf('form_designer.fields.TemplateCharField')(max_length=255, null=True, blank=True)),
+ ('mail_subject', self.gf('form_designer.fields.TemplateCharField')(max_length=255, null=True, blank=True)),
+ ('method', self.gf('django.db.models.fields.CharField')(default='POST', max_length=10)),
+ ('success_message', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+ ('error_message', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+ ('submit_label', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+ ('log_data', self.gf('django.db.models.fields.BooleanField')(default=True, blank=True)),
+ ('success_redirect', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True)),
+ ('success_clear', self.gf('django.db.models.fields.BooleanField')(default=True, blank=True)),
+ ('allow_get_initial', self.gf('django.db.models.fields.BooleanField')(default=True, blank=True)),
+ ('message_template', self.gf('form_designer.fields.TemplateTextField')(null=True, blank=True)),
+ ('form_template_name', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+ ))
+ db.send_create_signal('form_designer', ['FormDefinition'])
+
+ # Adding model 'FormLog'
+ db.create_table('form_designer_formlog', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)),
+ ('form_definition', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['form_designer.FormDefinition'])),
+ ('data', self.gf('picklefield.fields.PickledObjectField')(null=True, blank=True)),
+ ))
+ db.send_create_signal('form_designer', ['FormLog'])
+
+ # Adding model 'FormDefinitionField'
+ db.create_table('form_designer_formdefinitionfield', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('form_definition', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['form_designer.FormDefinition'])),
+ ('field_class', self.gf('django.db.models.fields.CharField')(max_length=32)),
+ ('position', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)),
+ ('name', self.gf('django.db.models.fields.SlugField')(max_length=255, db_index=True)),
+ ('label', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+ ('required', self.gf('django.db.models.fields.BooleanField')(default=True, blank=True)),
+ ('include_result', self.gf('django.db.models.fields.BooleanField')(default=True, blank=True)),
+ ('widget', self.gf('django.db.models.fields.CharField')(default='', max_length=255, null=True, blank=True)),
+ ('initial', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+ ('help_text', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+ ('choice_values', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+ ('choice_labels', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+ ('max_length', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)),
+ ('min_length', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)),
+ ('max_value', self.gf('django.db.models.fields.FloatField')(null=True, blank=True)),
+ ('min_value', self.gf('django.db.models.fields.FloatField')(null=True, blank=True)),
+ ('max_digits', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)),
+ ('decimal_places', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)),
+ ('regex', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+ ('choice_model', self.gf('form_designer.fields.ModelNameField')(max_length=255, null=True, blank=True)),
+ ('choice_model_empty_label', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+ ))
+ db.send_create_signal('form_designer', ['FormDefinitionField'])
+
+ # Adding model 'CMSFormDefinition'
+ db.create_table('cmsplugin_cmsformdefinition', (
+ ('cmsplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['cms.CMSPlugin'], unique=True, primary_key=True)),
+ ('form_definition', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['form_designer.FormDefinition'])),
+ ))
+ db.send_create_signal('form_designer', ['CMSFormDefinition'])
+
+
+ def backwards(self, orm):
+
+ # Deleting model 'FormDefinition'
+ db.delete_table('form_designer_formdefinition')
+
+ # Deleting model 'FormLog'
+ db.delete_table('form_designer_formlog')
+
+ # Deleting model 'FormDefinitionField'
+ db.delete_table('form_designer_formdefinitionfield')
+
+ # Deleting model 'CMSFormDefinition'
+ db.delete_table('cmsplugin_cmsformdefinition')
+
+
+ models = {
+ 'cms.cmsplugin': {
+ 'Meta': {'object_name': 'CMSPlugin'},
+ 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'language': ('django.db.models.fields.CharField', [], {'max_length': '5', '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'}),
+ 'publisher_is_draft': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True', 'blank': 'True'}),
+ 'publisher_public': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'publisher_draft'", 'unique': 'True', 'null': 'True', 'to': "orm['cms.CMSPlugin']"}),
+ 'publisher_state': ('django.db.models.fields.SmallIntegerField', [], {'default': '0', 'db_index': 'True'}),
+ 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
+ },
+ 'cms.placeholder': {
+ 'Meta': {'object_name': 'Placeholder'},
+ 'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slot': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'})
+ },
+ 'form_designer.cmsformdefinition': {
+ 'Meta': {'object_name': 'CMSFormDefinition', 'db_table': "'cmsplugin_cmsformdefinition'", '_ormbases': ['cms.CMSPlugin']},
+ 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}),
+ 'form_definition': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['form_designer.FormDefinition']"})
+ },
+ 'form_designer.formdefinition': {
+ 'Meta': {'object_name': 'FormDefinition'},
+ 'action': ('django.db.models.fields.URLField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'allow_get_initial': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+ 'error_message': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'form_template_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'log_data': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+ 'mail_from': ('form_designer.fields.TemplateCharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'mail_subject': ('form_designer.fields.TemplateCharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'mail_to': ('form_designer.fields.TemplateCharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'message_template': ('form_designer.fields.TemplateTextField', [], {'null': 'True', 'blank': 'True'}),
+ 'method': ('django.db.models.fields.CharField', [], {'default': "'POST'", 'max_length': '10'}),
+ 'name': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'submit_label': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'success_clear': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+ 'success_message': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'success_redirect': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
+ },
+ 'form_designer.formdefinitionfield': {
+ 'Meta': {'object_name': 'FormDefinitionField'},
+ 'choice_labels': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'choice_model': ('form_designer.fields.ModelNameField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'choice_model_empty_label': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'choice_values': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'decimal_places': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'field_class': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'form_definition': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['form_designer.FormDefinition']"}),
+ 'help_text': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'include_result': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+ 'initial': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'label': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'max_digits': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'max_length': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'max_value': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
+ 'min_length': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'min_value': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'position': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'regex': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'required': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+ 'widget': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'null': 'True', 'blank': 'True'})
+ },
+ 'form_designer.formlog': {
+ 'Meta': {'object_name': 'FormLog'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'data': ('picklefield.fields.PickledObjectField', [], {'null': 'True', 'blank': 'True'}),
+ 'form_definition': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['form_designer.FormDefinition']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+ }
+ }
+
+ complete_apps = ['form_designer']
diff --git a/form_designer/migrations/0002_auto__chg_field_formdefinitionfield_initial.py b/form_designer/migrations/0002_auto__chg_field_formdefinitionfield_initial.py
new file mode 100644
index 00000000..5fa38e27
--- /dev/null
+++ b/form_designer/migrations/0002_auto__chg_field_formdefinitionfield_initial.py
@@ -0,0 +1,104 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Changing field 'FormDefinitionField.initial'
+ db.alter_column('form_designer_formdefinitionfield', 'initial', self.gf('django.db.models.fields.TextField')(null=True, blank=True))
+
+
+ def backwards(self, orm):
+
+ # Changing field 'FormDefinitionField.initial'
+ db.alter_column('form_designer_formdefinitionfield', 'initial', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True))
+
+
+ models = {
+ 'cms.cmsplugin': {
+ 'Meta': {'object_name': 'CMSPlugin'},
+ 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'language': ('django.db.models.fields.CharField', [], {'max_length': '5', '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'}),
+ 'publisher_is_draft': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True', 'blank': 'True'}),
+ 'publisher_public': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'publisher_draft'", 'unique': 'True', 'null': 'True', 'to': "orm['cms.CMSPlugin']"}),
+ 'publisher_state': ('django.db.models.fields.SmallIntegerField', [], {'default': '0', 'db_index': 'True'}),
+ 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
+ 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
+ },
+ 'cms.placeholder': {
+ 'Meta': {'object_name': 'Placeholder'},
+ 'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slot': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'})
+ },
+ 'form_designer.cmsformdefinition': {
+ 'Meta': {'object_name': 'CMSFormDefinition', 'db_table': "'cmsplugin_cmsformdefinition'", '_ormbases': ['cms.CMSPlugin']},
+ 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}),
+ 'form_definition': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['form_designer.FormDefinition']"})
+ },
+ 'form_designer.formdefinition': {
+ 'Meta': {'object_name': 'FormDefinition'},
+ 'action': ('django.db.models.fields.URLField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'allow_get_initial': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+ 'error_message': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'form_template_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'log_data': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+ 'mail_from': ('form_designer.fields.TemplateCharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'mail_subject': ('form_designer.fields.TemplateCharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'mail_to': ('form_designer.fields.TemplateCharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'message_template': ('form_designer.fields.TemplateTextField', [], {'null': 'True', 'blank': 'True'}),
+ 'method': ('django.db.models.fields.CharField', [], {'default': "'POST'", 'max_length': '10'}),
+ 'name': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
+ 'submit_label': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'success_clear': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+ 'success_message': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'success_redirect': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
+ },
+ 'form_designer.formdefinitionfield': {
+ 'Meta': {'object_name': 'FormDefinitionField'},
+ 'choice_labels': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'choice_model': ('form_designer.fields.ModelNameField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'choice_model_empty_label': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'choice_values': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'decimal_places': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'field_class': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'form_definition': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['form_designer.FormDefinition']"}),
+ 'help_text': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'include_result': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+ 'initial': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'label': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'max_digits': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'max_length': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'max_value': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
+ 'min_length': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'min_value': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'position': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'regex': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'required': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+ 'widget': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'null': 'True', 'blank': 'True'})
+ },
+ 'form_designer.formlog': {
+ 'Meta': {'object_name': 'FormLog'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'data': ('picklefield.fields.PickledObjectField', [], {'null': 'True', 'blank': 'True'}),
+ 'form_definition': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['form_designer.FormDefinition']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+ }
+ }
+
+ complete_apps = ['form_designer']
diff --git a/form_designer/migrations/__init__.py b/form_designer/migrations/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/form_designer/models.py b/form_designer/models.py
index 12d78a55..767de563 100644
--- a/form_designer/models.py
+++ b/form_designer/models.py
@@ -1,13 +1,34 @@
+import re
+
from django.db import models
from django.utils.translation import ugettext, ugettext_lazy as _
from django.forms import widgets
from django.core.mail import send_mail
-from django.conf import settings
-from form_designer import app_settings
-import re
-from pickled_object_field import PickledObjectField
-from model_name_field import ModelNameField
-from template_field import TemplateTextField, TemplateCharField
+from django.conf import settings as django_settings
+from django.core.exceptions import ImproperlyConfigured
+from django.utils.importlib import import_module
+
+from picklefield.fields import PickledObjectField
+
+from form_designer.fields import TemplateTextField, TemplateCharField, ModelNameField
+from form_designer import settings
+
+def get_class(import_path):
+ try:
+ dot = import_path.rindex('.')
+ except ValueError:
+ raise ImproperlyConfigured("%s isn't a Python path." % import_path)
+ module, classname = import_path[:dot], import_path[dot + 1:]
+ try:
+ mod = import_module(module)
+ except ImportError, e:
+ raise ImproperlyConfigured('Error importing module %s: "%s"' %
+ (module, e))
+ try:
+ return getattr(mod, classname)
+ except AttributeError:
+ raise ImproperlyConfigured('Module "%s" does not define a "%s" '
+ 'class.' % (module, classname))
class FormDefinition(models.Model):
name = models.SlugField(_('Name'), max_length=255, unique=True)
@@ -21,11 +42,11 @@ class FormDefinition(models.Model):
error_message = models.CharField(_('Error message'), max_length=255, blank=True, null=True)
submit_label = models.CharField(_('Submit button label'), max_length=255, blank=True, null=True)
log_data = models.BooleanField(_('Log form data'), help_text=_('Logs all form submissions to the database.'), default=True)
- success_redirect = models.BooleanField(_('Redirect after success'), help_text=_('You should install django_notify if you want to enable this.') if not 'django_notify' in settings.INSTALLED_APPS else None, default=False)
+ success_redirect = models.BooleanField(_('Redirect after success'), default=False)
success_clear = models.BooleanField(_('Clear form after success'), default=True)
allow_get_initial = models.BooleanField(_('Allow initial values via URL'), help_text=_('If enabled, you can fill in form fields by adding them to the query string.'), default=True)
message_template = TemplateTextField(_('Message template'), help_text=_('Your form fields are available as template context. Example: "{{ message }}" if you have a field named `message`. To iterate over all fields, use the variable `data` (a list containing a dictionary for each form field, each containing the elements `name`, `label`, `value`).'), blank=True, null=True)
- form_template_name = models.CharField(_('Form template'), max_length=255, choices=app_settings.get('FORM_DESIGNER_FORM_TEMPLATES'), blank=True, null=True)
+ form_template_name = models.CharField(_('Form template'), max_length=255, choices=settings.FORM_TEMPLATES, blank=True, null=True)
class Meta:
verbose_name = _('Form')
@@ -36,7 +57,7 @@ def get_field_dict(self):
for field in self.formdefinitionfield_set.all():
dict[field.name] = field
return dict
-
+
def get_form_data(self, form):
data = []
field_dict = self.get_field_dict()
@@ -49,7 +70,7 @@ def get_form_data(self, form):
value = value.__form_data__()
data.append({'name': key, 'label': form.fields[key].label, 'value': value})
return data
-
+
def get_form_data_dict(self, form_data):
dict = {}
for field in form_data:
@@ -90,34 +111,39 @@ def string_template_replace(self, text, context_dict):
except TemplateSyntaxError:
return text
- def send_mail(self, form):
+ def send_mail(self, form, files=[]):
form_data = self.get_form_data(form)
message = self.compile_message(form_data)
context_dict = self.get_form_data_dict(form_data)
- import re
+ import re
mail_to = re.compile('\s*[,;]+\s*').split(self.mail_to)
for key, email in enumerate(mail_to):
mail_to[key] = self.string_template_replace(email, context_dict)
-
+
mail_from = self.mail_from or None
if mail_from:
mail_from = self.string_template_replace(mail_from, context_dict)
-
+
if self.mail_subject:
mail_subject = self.string_template_replace(self.mail_subject, context_dict)
else:
mail_subject = self.title
-
+
import logging
logging.debug('Mail: '+repr(mail_from)+' --> '+repr(mail_to));
-
- from django.core.mail import send_mail
- send_mail(mail_subject, message, mail_from or None, mail_to, fail_silently=False)
+
+ from django.core.mail import EmailMessage
+ message = EmailMessage(mail_subject, message, mail_from or None, mail_to)
+
+ for file_path in files:
+ message.attach_file(file_path)
+
+ message.send(fail_silently=False)
@property
def submit_flag_name(self):
- name = app_settings.get('FORM_DESIGNER_SUBMIT_FLAG_NAME') % self.name
+ name = settings.SUBMIT_FLAG_NAME % self.name
while self.formdefinitionfield_set.filter(name__exact=name).count() > 0:
name += '_'
return name
@@ -135,14 +161,14 @@ class Meta:
class FormDefinitionField(models.Model):
form_definition = models.ForeignKey(FormDefinition)
- field_class = models.CharField(_('Field class'), choices=app_settings.get('FORM_DESIGNER_FIELD_CLASSES'), max_length=32)
+ field_class = models.CharField(_('Field class'), choices=settings.FIELD_CLASSES, max_length=32)
position = models.IntegerField(_('Position'), blank=True, null=True)
name = models.SlugField(_('Name'), max_length=255)
label = models.CharField(_('Label'), max_length=255, blank=True, null=True)
required = models.BooleanField(_('Required'), default=True)
include_result = models.BooleanField(_('Include in result'), help_text=('If this is disabled, the field value will not be included in logs and e-mails generated from form data.'), default=True)
- widget = models.CharField(_('Widget'), default='', choices=app_settings.get('FORM_DESIGNER_WIDGET_CLASSES'), max_length=255, blank=True, null=True)
+ widget = models.CharField(_('Widget'), default='', choices=settings.WIDGET_CLASSES, max_length=255, blank=True, null=True)
initial = models.TextField(_('Initial value'), blank=True, null=True)
help_text = models.CharField(_('Help text'), max_length=255, blank=True, null=True)
@@ -158,7 +184,7 @@ class FormDefinitionField(models.Model):
regex = models.CharField(_('Regular Expression'), max_length=255, blank=True, null=True)
- choice_model_choices = app_settings.get('FORM_DESIGNER_CHOICE_MODEL_CHOICES')
+ choice_model_choices = settings.CHOICE_MODEL_CHOICES
choice_model = ModelNameField(_('Data model'), max_length=255, blank=True, null=True, choices=choice_model_choices, help_text=('your_app.models.ModelName' if not choice_model_choices else None))
choice_model_empty_label = models.CharField(_('Empty label'), max_length=255, blank=True, null=True)
@@ -174,7 +200,7 @@ def save(self):
def ____init__(self, field_class=None, name=None, required=None, widget=None, label=None, initial=None, help_text=None, *args, **kwargs):
super(FormDefinitionField, self).__init__(*args, **kwargs)
self.name = name
- self.field_class = field_class
+ self.field_class = field_class
self.required = required
self.widget = widget
self.label = label
@@ -188,20 +214,20 @@ def get_form_field_init_args(self):
'initial': self.initial if self.initial else None,
'help_text': self.help_text,
}
-
- if self.field_class in ('forms.CharField', 'forms.EmailField', 'forms.RegexField'):
+
+ if self.field_class in ('django.forms.CharField', 'django.forms.EmailField', 'django.forms.RegexField'):
args.update({
'max_length': self.max_length,
'min_length': self.min_length,
})
- if self.field_class in ('forms.IntegerField', 'forms.DecimalField'):
+ if self.field_class in ('django.forms.IntegerField', 'django.forms.DecimalField'):
args.update({
'max_value': int(self.max_value) if self.max_value != None else None,
'min_value': int(self.min_value) if self.min_value != None else None,
})
- if self.field_class == 'forms.DecimalField':
+ if self.field_class == 'django.forms.DecimalField':
args.update({
'max_value': self.max_value,
'min_value': self.min_value,
@@ -209,13 +235,13 @@ def get_form_field_init_args(self):
'decimal_places': self.decimal_places,
})
- if self.field_class == 'forms.RegexField':
+ if self.field_class == 'django.forms.RegexField':
if self.regex:
args.update({
'regex': self.regex
})
- if self.field_class in ('forms.ChoiceField', 'forms.MultipleChoiceField'):
+ if self.field_class in ('django.forms.ChoiceField', 'django.forms.MultipleChoiceField'):
if self.choice_values:
choices = []
regex = re.compile('[\s]*\n[\s]*')
@@ -231,21 +257,21 @@ def get_form_field_init_args(self):
'choices': tuple(choices)
})
- if self.field_class in ('forms.ModelChoiceField', 'forms.ModelMultipleChoiceField'):
+ if self.field_class in ('django.forms.ModelChoiceField', 'django.forms.ModelMultipleChoiceField'):
args.update({
'queryset': ModelNameField.get_model_from_string(self.choice_model).objects.all()
})
-
- if self.field_class == 'forms.ModelChoiceField':
+
+ if self.field_class == 'django.forms.ModelChoiceField':
args.update({
'empty_label': self.choice_model_empty_label
})
if self.widget:
args.update({
- 'widget': eval(self.widget)()
+ 'widget': get_class(self.widget)()
})
-
+
return args
class Meta:
@@ -256,7 +282,7 @@ class Meta:
def __unicode__(self):
return self.label if self.label else self.name
-if 'cms' in settings.INSTALLED_APPS:
+if 'cms' in django_settings.INSTALLED_APPS:
from cms.models import CMSPlugin
class CMSFormDefinition(CMSPlugin):
@@ -264,3 +290,7 @@ class CMSFormDefinition(CMSPlugin):
def __unicode__(self):
return self.form_definition.__unicode__()
+
+if 'south' in django_settings.INSTALLED_APPS:
+ from south.modelsinspector import add_introspection_rules
+ add_introspection_rules([], ["^form_designer\.fields\..*"])
diff --git a/form_designer/pickled_object_field.py b/form_designer/pickled_object_field.py
deleted file mode 100644
index 8ccd8ff6..00000000
--- a/form_designer/pickled_object_field.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# http://www.smipple.net/snippet/IanLewis/Django%20Pickled%20Object%20Field
-
-try:
- import cPickle as pickle
-except ImportError:
- import pickle
-
-import base64
-
-from django.db import models
-
-class PickledObjectField(models.TextField):
- __metaclass__ = models.SubfieldBase
-
- def to_python(self, value):
- if value is None: return None
- if not isinstance(value, basestring): return value
- try:
- return pickle.loads(base64.b64decode(value))
- except:
- return None
-
- def get_db_prep_save(self, value):
- if value is None: return
- return base64.b64encode(pickle.dumps(value))
diff --git a/form_designer/settings.py b/form_designer/settings.py
new file mode 100644
index 00000000..c2ee38a2
--- /dev/null
+++ b/form_designer/settings.py
@@ -0,0 +1,65 @@
+from django.conf import settings
+from django.utils.translation import ugettext_lazy as _
+
+MEDIA_URL = getattr(settings, 'FORM_DESIGNER_MEDIA_URL', '%sform_designer/' % settings.MEDIA_URL)
+
+FIELD_CLASSES = getattr(settings, 'FORM_DESIGNER_FIELD_CLASSES', (
+ ('django.forms.CharField', _('Text')),
+ ('django.forms.EmailField', _('E-mail address')),
+ ('django.forms.URLField', _('Web address')),
+ ('django.forms.IntegerField', _('Number')),
+ ('django.forms.DecimalField', _('Decimal number')),
+ ('django.forms.BooleanField', _('Yes/No')),
+ ('django.forms.DateField', _('Date')),
+ ('django.forms.DateTimeField', _('Date & time')),
+ ('django.forms.TimeField', _('Time')),
+ ('django.forms.ChoiceField', _('Choice')),
+ ('django.forms.MultipleChoiceField', _('Multiple Choice')),
+ ('django.forms.ModelChoiceField', _('Model Choice')),
+ ('django.forms.ModelMultipleChoiceField', _('Model Multiple Choice')),
+ ('django.forms.RegexField', _('Regex')),
+ ('django.forms.FileField', _('File')),
+ # ('captcha.fields.CaptchaField', _('Captcha')),
+))
+
+WIDGET_CLASSES = getattr(settings, 'FORM_DESIGNER_WIDGET_CLASSES', (
+ ('', _('Default')),
+ ('django.forms.widgets.Textarea', _('Text area')),
+ ('django.forms.widgets.PasswordInput', _('Password input')),
+ ('django.forms.widgets.HiddenInput', _('Hidden input')),
+ ('django.forms.widgets.RadioSelect', _('Radio button')),
+))
+
+FORM_TEMPLATES = getattr(settings, 'FORM_DESIGNER_FORM_TEMPLATES', (
+ ('', _('Default')),
+ ('html/formdefinition/forms/as_p.html', _('as paragraphs')),
+ ('html/formdefinition/forms/as_table.html', _('as table')),
+ ('html/formdefinition/forms/as_ul.html', _('as unordered list')),
+ ('html/formdefinition/forms/custom.html', _('custom implementation')),
+))
+
+# Sequence of two-tuples like (('your_app.models.ModelName', 'My Model'), ...) for limiting the models available to ModelChoiceField and ModelMultipleChoiceField.
+# If None, any model can be chosen by entering it as a string
+CHOICE_MODEL_CHOICES = getattr(settings, 'FORM_DESIGNER_CHOICE_MODEL_CHOICES', None)
+
+DEFAULT_FORM_TEMPLATE = getattr(settings, 'FORM_DESIGNER_DEFAULT_FORM_TEMPLATE', 'html/formdefinition/forms/as_p.html')
+
+# semicolon is Microsoft Excel default
+CSV_EXPORT_DELIMITER = getattr(settings, 'FORM_DESIGNER_CSV_EXPORT_DELIMITER', ';')
+
+# include log timestamp in export
+CSV_EXPORT_INCLUDE_CREATED = getattr(settings, 'FORM_DESIGNER_CSV_EXPORT_INCLUDE_CREATED', True)
+
+CSV_EXPORT_INCLUDE_PK = getattr(settings, 'FORM_DESIGNER_CSV_EXPORT_INCLUDE_PK', True)
+
+# include field labels/names in first row if exporting logs for one form only
+CSV_EXPORT_INCLUDE_HEADER = getattr(settings, 'FORM_DESIGNER_CSV_EXPORT_INCLUDE_HEADER', True)
+
+# include form title if exporting logs for more than one form
+CSV_EXPORT_INCLUDE_FORM = getattr(settings, 'FORM_DESIGNER_CSV_EXPORT_INCLUDE_FORM', True)
+
+CSV_EXPORT_FILENAME = getattr(settings, 'FORM_DESIGNER_CSV_EXPORT_FILENAME', 'export.csv')
+
+CSV_EXPORT_ENCODING = getattr(settings, 'FORM_DESIGNER_CSV_EXPORT_ENCODING', 'utf-8')
+
+SUBMIT_FLAG_NAME = getattr(settings, 'FORM_DESIGNER_SUBMIT_FLAG_NAME', 'submit__%s')
diff --git a/form_designer/template_field.py b/form_designer/template_field.py
deleted file mode 100644
index a812d1ec..00000000
--- a/form_designer/template_field.py
+++ /dev/null
@@ -1,34 +0,0 @@
-from django.db import models
-from django import forms
-
-class TemplateFormField(forms.CharField):
-
- def clean(self, value):
- """
- Validates that the input can be compiled as a template.
- """
- value = super(TemplateFormField, self).clean(value)
- from django.template import Template, TemplateSyntaxError
- try:
- Template(value)
- except TemplateSyntaxError as error:
- raise forms.ValidationError(error)
- return value
-
-class TemplateCharField(models.CharField):
-
- def formfield(self, **kwargs):
- # This is a fairly standard way to set up some defaults
- # while letting the caller override them.
- defaults = {'form_class': TemplateFormField}
- defaults.update(kwargs)
- return super(TemplateCharField, self).formfield(**defaults)
-
-class TemplateTextField(models.TextField):
-
- def formfield(self, **kwargs):
- # This is a fairly standard way to set up some defaults
- # while letting the caller override them.
- defaults = {'form_class': TemplateFormField}
- defaults.update(kwargs)
- return super(TemplateTextField, self).formfield(**defaults)
diff --git a/form_designer/templates/admin/form_designer/formlog/change_list.html b/form_designer/templates/admin/form_designer/formlog/change_list.html
index 84e0e8a3..6ad372c0 100644
--- a/form_designer/templates/admin/form_designer/formlog/change_list.html
+++ b/form_designer/templates/admin/form_designer/formlog/change_list.html
@@ -10,6 +10,13 @@
{% endif %}
+ {% if export_xls_url %}
+
+
+ {% trans "Export XLS" %}
+
+
+ {% endif %}
{% endblock %}
diff --git a/form_designer/templates/html/formdefinition/forms/as_p.html b/form_designer/templates/html/formdefinition/forms/as_p.html
new file mode 100644
index 00000000..57a046b9
--- /dev/null
+++ b/form_designer/templates/html/formdefinition/forms/as_p.html
@@ -0,0 +1,12 @@
+{% load i18n %}
+{{ form_definition.title }}
+{% if message %}
+
+{% endif %}
+
diff --git a/form_designer/templates/html/formdefinition/forms/as_table.html b/form_designer/templates/html/formdefinition/forms/as_table.html
new file mode 100644
index 00000000..f9f6623f
--- /dev/null
+++ b/form_designer/templates/html/formdefinition/forms/as_table.html
@@ -0,0 +1,12 @@
+{% load i18n %}
+{{ form_definition.title }}
+{% if message %}
+
+{% endif %}
+
diff --git a/form_designer/templates/html/formdefinition/forms/as_u.html b/form_designer/templates/html/formdefinition/forms/as_u.html
new file mode 100644
index 00000000..fbdaafa3
--- /dev/null
+++ b/form_designer/templates/html/formdefinition/forms/as_u.html
@@ -0,0 +1,12 @@
+{% load i18n %}
+{{ form_definition.title }}
+{% if message %}
+
+{% endif %}
+
\ No newline at end of file
diff --git a/form_designer/templates/html/formdefinition/forms/as_ul.html b/form_designer/templates/html/formdefinition/forms/as_ul.html
new file mode 100644
index 00000000..418d5d95
--- /dev/null
+++ b/form_designer/templates/html/formdefinition/forms/as_ul.html
@@ -0,0 +1,12 @@
+{% load i18n %}
+{{ form_definition.title }}
+{% if message %}
+
+{% endif %}
+
diff --git a/form_designer/templates/html/formdefinition/forms/custom.html b/form_designer/templates/html/formdefinition/forms/custom.html
new file mode 100644
index 00000000..313a357d
--- /dev/null
+++ b/form_designer/templates/html/formdefinition/forms/custom.html
@@ -0,0 +1,42 @@
+{% load i18n %}
+{% load widget_type %}
+
+{% if message %}
+
+{% endif %}
+
+
+
\ No newline at end of file
diff --git a/form_designer/templates/txt/formdefinition/data_message.txt b/form_designer/templates/txt/formdefinition/data_message.txt
index c5cc8fe2..248e62b6 100644
--- a/form_designer/templates/txt/formdefinition/data_message.txt
+++ b/form_designer/templates/txt/formdefinition/data_message.txt
@@ -1,2 +1,3 @@
-{% load friendly %}{% for item in data %}{% if item.label %}{{ item.label }}{% else %}{{ item.name }}{% endif %}: {{ item.value|friendly }}
+{% load friendly %}{% for item in data %}{% if item.label %}{{ item.label }}{% else %}{{ item.name }}{% endif %}: {{ item.value|friendly|safe }}
+
{% endfor %}
\ No newline at end of file
diff --git a/form_designer/templatetags/friendly.py b/form_designer/templatetags/friendly.py
index 09206a9a..ab65bf76 100644
--- a/form_designer/templatetags/friendly.py
+++ b/form_designer/templatetags/friendly.py
@@ -3,11 +3,13 @@
from django.utils.translation import ugettext_lazy as _
from django.template.defaultfilters import yesno
+register = template.Library()
+
# Returns a more "human-friendly" representation of value than repr()
def friendly(value):
if type(value) is QuerySet:
qs = value
- value = []
+ value = []
for object in qs:
value.append(object.__unicode__())
if type(value) is list:
@@ -18,5 +20,4 @@ def friendly(value):
value = unicode(value)
return value
-register = template.Library()
register.filter(friendly)
\ No newline at end of file
diff --git a/form_designer/templatetags/widget_type.py b/form_designer/templatetags/widget_type.py
new file mode 100644
index 00000000..45803cf9
--- /dev/null
+++ b/form_designer/templatetags/widget_type.py
@@ -0,0 +1,6 @@
+from django import template
+register = template.Library()
+
+@register.filter('field_type')
+def field_type(obj):
+ return obj.__class__.__name__
diff --git a/form_designer/tests.py b/form_designer/tests.py
deleted file mode 100644
index 2247054b..00000000
--- a/form_designer/tests.py
+++ /dev/null
@@ -1,23 +0,0 @@
-"""
-This file demonstrates two different styles of tests (one doctest and one
-unittest). These will both pass when you run "manage.py test".
-
-Replace these with more appropriate tests for your application.
-"""
-
-from django.test import TestCase
-
-class SimpleTest(TestCase):
- def test_basic_addition(self):
- """
- Tests that 1 + 1 always equals 2.
- """
- self.failUnlessEqual(1 + 1, 2)
-
-__test__ = {"doctest": """
-Another way to test that 1 + 1 is equal to 2.
-
->>> 1 + 1 == 2
-True
-"""}
-
diff --git a/form_designer/urls.py b/form_designer/urls.py
index e0d46a6e..2e915f2d 100644
--- a/form_designer/urls.py
+++ b/form_designer/urls.py
@@ -1,4 +1,4 @@
-from django.conf.urls.defaults import *
+from django.conf.urls.defaults import patterns, url
urlpatterns = patterns('',
url(r'^(?P[-\w]+)/$', 'form_designer.views.detail', name='form_designer_detail'),
diff --git a/form_designer/views.py b/form_designer/views.py
index 09cfded8..336a2e87 100644
--- a/form_designer/views.py
+++ b/form_designer/views.py
@@ -1,64 +1,74 @@
from django.shortcuts import get_object_or_404, render_to_response
from django.template import RequestContext
-from django.db import models
-from form_designer.models import FormDefinition
from django.utils.translation import ugettext as _
-from django import forms
-from django.forms import widgets
from django.http import HttpResponseRedirect
from django.conf import settings
-from form_designer import app_settings
+from django.contrib import messages
+from django.core.context_processors import csrf
-class DesignedForm(forms.Form):
- def __init__(self, form_definition, initial_data=None, *args, **kwargs):
- super(DesignedForm, self).__init__(*args, **kwargs)
- for def_field in form_definition.formdefinitionfield_set.all():
- self.add_defined_field(def_field, initial_data)
- self.fields[form_definition.submit_flag_name] = forms.BooleanField(required=False, initial=1, widget=widgets.HiddenInput)
+import os
+import random
+from datetime import datetime
- def add_defined_field(self, def_field, initial_data=None):
- if initial_data and initial_data.has_key(def_field.name):
- if not def_field.field_class in ('forms.MultipleChoiceField', 'forms.ModelMultipleChoiceField'):
- def_field.initial = initial_data.get(def_field.name)
- else:
- def_field.initial = initial_data.getlist(def_field.name)
- self.fields[def_field.name] = eval(def_field.field_class)(**def_field.get_form_field_init_args())
+from form_designer.forms import DesignedForm
+from form_designer.models import FormDefinition
def process_form(request, form_definition, context={}, is_cms_plugin=False):
success_message = form_definition.success_message or _('Thank you, the data was submitted successfully.')
error_message = form_definition.error_message or _('The data could not be submitted, please try again.')
message = None
-
+ form_error = False
+ form_success = False
is_submit = False
# If the form has been submitted...
if request.method == 'POST' and request.POST.get(form_definition.submit_flag_name):
- form = DesignedForm(form_definition, None, request.POST)
+ form = DesignedForm(form_definition, None, request.POST, request.FILES)
is_submit = True
if request.method == 'GET' and request.GET.get(form_definition.submit_flag_name):
form = DesignedForm(form_definition, None, request.GET)
is_submit = True
-
+
if is_submit:
if form.is_valid():
+ # Handle file uploads
+ files = []
+ if hasattr(request, 'FILES'):
+ for file_key in request.FILES:
+ file_obj = request.FILES[file_key]
+ file_name = '%s.%s_%s' % (
+ datetime.now().strftime('%Y%m%d'),
+ random.randrange(0, 10000),
+ file_obj.name,
+ )
+
+ if not os.path.exists(os.path.join(settings.MEDIA_ROOT, 'contact_form')):
+ os.mkdir(os.path.join(settings.MEDIA_ROOT, 'contact_form'))
+
+ destination = open(os.path.join(settings.MEDIA_ROOT, 'contact_form', file_name), 'wb+')
+ for chunk in file_obj.chunks():
+ destination.write(chunk)
+ destination.close()
+
+ form.cleaned_data[file_key] = os.path.join(settings.MEDIA_URL, 'contact_form', file_name)
+ files.append(os.path.join(settings.MEDIA_ROOT, 'contact_form', file_name))
+
# Successful submission
- if 'django_notify' in settings.INSTALLED_APPS:
- request.notifications.success(success_message)
- else:
- message = success_message
+ messages.success(request, success_message)
+ message = success_message
+ form_success = True
if form_definition.log_data:
form_definition.log(form)
if form_definition.mail_to:
- form_definition.send_mail(form)
+ form_definition.send_mail(form, files)
if form_definition.success_redirect and not is_cms_plugin:
# TODO Redirection does not work for cms plugin
return HttpResponseRedirect(form_definition.action or '?')
if form_definition.success_clear:
form = DesignedForm(form_definition) # clear form
else:
- if 'django_notify' in settings.INSTALLED_APPS:
- request.notifications.error(error_message)
- else:
- message = error_message
+ form_error = True
+ messages.error(request, error_message)
+ message = error_message
else:
if form_definition.allow_get_initial:
form = DesignedForm(form_definition, initial_data=request.GET)
@@ -67,10 +77,12 @@ def process_form(request, form_definition, context={}, is_cms_plugin=False):
context.update({
'message': message,
+ 'form_error': form_error,
+ 'form_success': form_success,
'form': form,
'form_definition': form_definition
})
-
+ context.update(csrf(request))
return context
def detail(request, object_name):
@@ -78,8 +90,8 @@ def detail(request, object_name):
result = process_form(request, form_definition)
if isinstance(result, HttpResponseRedirect):
return result
- else:
- result.update({
- 'form_template': form_definition.form_template_name or app_settings.get('FORM_DESIGNER_DEFAULT_FORM_TEMPLATE')
- })
- return render_to_response('html/formdefinition/detail.html', result, context_instance=RequestContext(request))
+ result.update({
+ 'form_template': form_definition.form_template_name or settings.DEFAULT_FORM_TEMPLATE
+ })
+ return render_to_response('html/formdefinition/detail.html', result,
+ context_instance=RequestContext(request))
diff --git a/setup.py b/setup.py
new file mode 100644
index 00000000..1ff6abc1
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,44 @@
+# encoding=utf8
+import os
+from distutils.core import setup
+
+def read(fname):
+ return open(os.path.join(os.path.dirname(__file__), fname)).read()
+
+README = read('README.md')
+
+setup(
+ name = "django-form-designer",
+ version = "0.1a8",
+ url = 'http://github.com/philomat/django-form-designer',
+ license = 'BSD',
+ description = "Design contact forms, search forms etc from the Django admin, without writing any code. Integrates with Django CMS.",
+ long_description = README,
+
+ author = u'Samuel Lüscher',
+ author_email = 'philomat@popkultur.net',
+ packages = [
+ 'form_designer',
+ 'form_designer.migrations',
+ 'form_designer.templatetags',
+ ],
+ package_data = {
+ 'form_designer': [
+ 'media/form_designer/js/*.js',
+ 'templates/admin/form_designer/formlog/change_list.html',
+ 'templates/html/formdefinition/*.html',
+ 'templates/html/formdefinition/forms/*.html',
+ 'templates/txt/formdefinition/*.txt',
+ 'locale/*/LC_MESSAGES/*',
+ ],
+ },
+ classifiers = [
+ 'Development Status :: 4 - Beta',
+ 'Framework :: Django',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: BSD License',
+ 'Operating System :: OS Independent',
+ 'Programming Language :: Python',
+ 'Topic :: Internet :: WWW/HTTP',
+ ]
+)