├── forms_builder ├── forms │ ├── __init__.py │ ├── migrations │ │ ├── __init__.py │ │ ├── 0002_auto_20160418_0120.py │ │ └── 0001_initial.py │ ├── templatetags │ │ ├── __init__.py │ │ └── forms_builder_tags.py │ ├── templates │ │ ├── email_extras │ │ │ ├── base.txt │ │ │ ├── base.html │ │ │ ├── form_response_copies.html │ │ │ ├── form_response_copies.txt │ │ │ ├── form_response.txt │ │ │ └── form_response.html │ │ ├── admin │ │ │ └── forms │ │ │ │ ├── change_list.html │ │ │ │ ├── change_form.html │ │ │ │ └── entries.html │ │ └── forms │ │ │ ├── form_sent.html │ │ │ ├── includes │ │ │ └── built_form.html │ │ │ └── form_detail.html │ ├── locale │ │ ├── de │ │ │ └── LC_MESSAGES │ │ │ │ ├── django.mo │ │ │ │ └── django.po │ │ ├── es │ │ │ └── LC_MESSAGES │ │ │ │ ├── django.mo │ │ │ │ └── django.po │ │ ├── fr │ │ │ └── LC_MESSAGES │ │ │ │ ├── django.mo │ │ │ │ └── django.po │ │ ├── nb │ │ │ └── LC_MESSAGES │ │ │ │ ├── django.mo │ │ │ │ └── django.po │ │ ├── nl │ │ │ └── LC_MESSAGES │ │ │ │ ├── django.mo │ │ │ │ └── django.po │ │ ├── pl │ │ │ └── LC_MESSAGES │ │ │ │ ├── django.mo │ │ │ │ └── django.po │ │ ├── ru │ │ │ └── LC_MESSAGES │ │ │ │ ├── django.mo │ │ │ │ └── django.po │ │ ├── zh │ │ │ └── LC_MESSAGES │ │ │ │ ├── django.mo │ │ │ │ └── django.po │ │ └── pt_BR │ │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── signals.py │ ├── urls.py │ ├── utils.py │ ├── settings.py │ ├── fields.py │ ├── views.py │ ├── tests.py │ ├── admin.py │ ├── models.py │ └── forms.py ├── example_project │ ├── __init__.py │ ├── urls.py │ ├── manage.py │ ├── templates │ │ └── index.html │ └── settings.py └── __init__.py ├── docs ├── index.rst ├── img │ ├── fields.png │ └── report.png └── conf.py ├── setup.cfg ├── MANIFEST.in ├── .hgignore ├── .gitignore ├── .hgtags ├── .travis.yml ├── AUTHORS ├── LICENSE ├── setup.py └── README.rst /forms_builder/forms/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../README.rst -------------------------------------------------------------------------------- /forms_builder/example_project/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /forms_builder/forms/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [wheel] 2 | universal = 1 3 | -------------------------------------------------------------------------------- /forms_builder/forms/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /forms_builder/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = "0.13.0" 2 | -------------------------------------------------------------------------------- /docs/img/fields.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/django-forms-builder/master/docs/img/fields.png -------------------------------------------------------------------------------- /docs/img/report.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/django-forms-builder/master/docs/img/report.png -------------------------------------------------------------------------------- /forms_builder/forms/templates/email_extras/base.txt: -------------------------------------------------------------------------------- 1 | {% block main %}{% endblock %} 2 | 3 | http://{{ request.get_host }} 4 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # This file is automatically generated via sphinx-me 2 | from sphinx_me import setup_conf; setup_conf(globals()) 3 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include README.rst 3 | recursive-include forms_builder * 4 | recursive-exclude */example_project/static * 5 | -------------------------------------------------------------------------------- /forms_builder/forms/locale/de/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/django-forms-builder/master/forms_builder/forms/locale/de/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /forms_builder/forms/locale/es/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/django-forms-builder/master/forms_builder/forms/locale/es/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /forms_builder/forms/locale/fr/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/django-forms-builder/master/forms_builder/forms/locale/fr/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /forms_builder/forms/locale/nb/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/django-forms-builder/master/forms_builder/forms/locale/nb/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /forms_builder/forms/locale/nl/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/django-forms-builder/master/forms_builder/forms/locale/nl/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /forms_builder/forms/locale/pl/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/django-forms-builder/master/forms_builder/forms/locale/pl/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /forms_builder/forms/locale/ru/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/django-forms-builder/master/forms_builder/forms/locale/ru/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /forms_builder/forms/locale/zh/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/django-forms-builder/master/forms_builder/forms/locale/zh/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /forms_builder/forms/locale/pt_BR/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chozabu/django-forms-builder/master/forms_builder/forms/locale/pt_BR/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /forms_builder/forms/templates/email_extras/base.html: -------------------------------------------------------------------------------- 1 | {% block main %}{% endblock %} 2 | 3 |
http://{{ request.get_host }} 4 | -------------------------------------------------------------------------------- /forms_builder/forms/templates/email_extras/form_response_copies.html: -------------------------------------------------------------------------------- 1 | {% extends "email_extras/form_response.html" %} 2 | 3 | {% block main %} 4 | {{ block.super }} 5 | {% endblock %} 6 | -------------------------------------------------------------------------------- /forms_builder/forms/templates/email_extras/form_response_copies.txt: -------------------------------------------------------------------------------- 1 | {% extends "email_extras/form_response.txt" %} 2 | 3 | {% block main %} 4 | {{ block.super }} 5 | {% endblock %} 6 | -------------------------------------------------------------------------------- /.hgignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | *.pyc 3 | *.pyo 4 | *.db 5 | .DS_Store 6 | .coverage 7 | local_settings.py 8 | build/* 9 | dist/* 10 | *.egg-info/* 11 | example_project/static 12 | 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.pyc 3 | *.pyo 4 | *.db 5 | .DS_Store 6 | .coverage 7 | .idea/ 8 | local_settings.py 9 | *.egg-info 10 | build/ 11 | dist/ 12 | forms_builder/example_project/static/ 13 | -------------------------------------------------------------------------------- /.hgtags: -------------------------------------------------------------------------------- 1 | c61aed49cbd9b1a37be2d0352fb49507357987d7 0.7.12 2 | 95e030ede47e7bb9486eceea61832a11c5788862 0.7.13 3 | 4f5e2bef4ff8c2d6d99e66ddabc2f784cae0338a 0.7.14 4 | cc7727ee029429e283815bdd71c806cee8bfb483 0.7.15 5 | -------------------------------------------------------------------------------- /forms_builder/forms/signals.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from django.dispatch import Signal 4 | 5 | form_invalid = Signal(providing_args=["form"]) 6 | form_valid = Signal(providing_args=["form", "entry"]) 7 | -------------------------------------------------------------------------------- /forms_builder/forms/templates/admin/forms/change_list.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/change_list.html" %} 2 | 3 | {% block extrahead %} 4 | {{ block.super }} 5 | 8 | {% endblock %} 9 | -------------------------------------------------------------------------------- /forms_builder/forms/templates/email_extras/form_response.txt: -------------------------------------------------------------------------------- 1 | {% extends "email_extras/base.txt" %} 2 | 3 | {% block main %}{% if message %} 4 | {{ message }} 5 | 6 | {% endif %}{% for field, value in fields %} 7 | {{ field }}: {{ value|safe }} 8 | {% endfor %} 9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /forms_builder/forms/urls.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from django.conf.urls import url 4 | 5 | from forms_builder.forms import views 6 | 7 | 8 | urlpatterns = [ 9 | url(r"(?P.*)/sent/$", views.form_sent, name="form_sent"), 10 | url(r"(?P.*)/$", views.form_detail, name="form_detail"), 11 | ] 12 | -------------------------------------------------------------------------------- /forms_builder/forms/templates/email_extras/form_response.html: -------------------------------------------------------------------------------- 1 | {% extends "email_extras/base.html" %} 2 | 3 | {% block main %} 4 | {% if message %}

{{ message }}

{% endif %} 5 | 6 | {% for field, value in fields %} 7 | 8 | 9 | 10 | 11 | {% endfor %} 12 |
{{ field }}:{{ value|linebreaks }}
13 | {% endblock %} 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | env: 3 | - DJANGO_VERSION=https://github.com/django/django/archive/stable/1.8.x.zip 4 | - DJANGO_VERSION=https://github.com/django/django/archive/stable/1.9.x.zip 5 | - DJANGO_VERSION=https://github.com/django/django/archive/stable/1.10.x.zip 6 | python: 7 | - "2.7" 8 | - "3.4" 9 | - "3.5" 10 | install: 11 | - pip install $DJANGO_VERSION 12 | - pip install . 13 | script: ./forms_builder/example_project/manage.py test 14 | -------------------------------------------------------------------------------- /forms_builder/forms/templates/forms/form_sent.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ form.title }} 5 | 11 | 12 | 13 |

{{ form.title }}

14 | {% if form.response %} 15 |

{{ form.response }}

16 | {% endif %} 17 | 18 | -------------------------------------------------------------------------------- /forms_builder/forms/templates/forms/includes/built_form.html: -------------------------------------------------------------------------------- 1 |

{{ form.title }}

2 | {% if form.intro %} 3 |

{{ form.intro }}

4 | {% endif %} 5 | {{ form_for_form.media }} 6 |
8 | {% csrf_token %} 9 | {{ form_for_form.as_p }} 10 |
 
11 | 12 |
13 | -------------------------------------------------------------------------------- /forms_builder/example_project/urls.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from django.conf.urls import include, url 4 | from django.contrib import admin 5 | from django.shortcuts import render 6 | 7 | from forms_builder.forms.models import Form 8 | from forms_builder.forms import urls as form_urls 9 | 10 | 11 | admin.autodiscover() 12 | 13 | urlpatterns = [ 14 | url(r'^admin/', include(admin.site.urls)), 15 | url(r'^forms/', include(form_urls)), 16 | url(r'^$', lambda request: render(request, "index.html", 17 | {"forms": Form.objects.all()})), 18 | ] 19 | -------------------------------------------------------------------------------- /forms_builder/forms/templates/forms/form_detail.html: -------------------------------------------------------------------------------- 1 | 2 | {% load forms_builder_tags %} 3 | 4 | 5 | {{ form.title }} 6 | 14 | 15 | 16 | {% render_built_form form %} 17 | 18 | -------------------------------------------------------------------------------- /forms_builder/forms/templates/admin/forms/change_form.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/change_form.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block object-tools-items %} 6 | 7 |
  • {% trans "View entries" %}
  • 8 | {{ block.super }} 9 | 10 | 18 | 19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /forms_builder/example_project/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from __future__ import absolute_import, unicode_literals 3 | 4 | import sys 5 | import os 6 | 7 | from settings import PROJECT_ROOT, PROJECT_DIRNAME 8 | 9 | sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../..')) 10 | sys.path.insert(0, os.path.abspath(os.path.join(PROJECT_ROOT, ".."))) 11 | 12 | if __name__ == "__main__": 13 | settings_module = "%s.settings" % PROJECT_DIRNAME 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", settings_module) 15 | from django.core.management import execute_from_command_line 16 | execute_from_command_line(sys.argv) 17 | -------------------------------------------------------------------------------- /forms_builder/forms/migrations/0002_auto_20160418_0120.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.4 on 2016-04-18 01:20 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('forms', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='form', 17 | name='email_from', 18 | field=models.EmailField(blank=True, help_text='The address the email will be sent from', max_length=254, verbose_name='From address'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /forms_builder/example_project/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ form.title }} 5 | 11 | 12 | 13 |

    Forms

    14 | {% for form in forms %} 15 |

    {{ form.title }}

    16 | {% empty %} 17 |

    No forms created. Go to the admin to create a form.

    18 | {% endfor %} 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | * Stephen McDonald 2 | * Chris Heisel 3 | * Diego Andres Sanabria Martin 4 | * Sindre Sorhus 5 | * Steffen Platte 6 | * Mike Bethany 7 | * Issac Kelly 8 | * Toby White 9 | * Martin Hebrank 10 | * Tyler Xing 11 | * Bojan Mihelac 12 | * Jacob Sunol 13 | * Maxim Sukharev 14 | * Santiago Piccinini 15 | * Dominique Guardiola 16 | * Robert Barsch 17 | * Jonathan Liuti 18 | * Mark Mukherjee 19 | * Fill Quazy 20 | * Sean Voss 21 | * Fabio Comuni 22 | * Eric Brelsford 23 | * Tiago Ilieve 24 | * Adam Gray 25 | * Sven Rojek 26 | * Michael Bauer 27 | * Eric Amador 28 | * Nikita Shalakov 29 | * Artur Barseghyan 30 | * Flávio Juvenal 31 | * Terry Hong 32 | * Damian Moore 33 | * Ace Han 34 | * Kamil Dębowski 35 | * Jaap Roes 36 | * Mathew Oakes 37 | * Serge Bobrovsky 38 | * Jasper Bok 39 | * Tai Lee 40 | * Bill Zhang 41 | * Ian Soboroff 42 | * Basil Shubin 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) Stephen McDonald and individual contributors. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 21 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | -------------------------------------------------------------------------------- /forms_builder/forms/utils.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from django.template.defaultfilters import slugify as django_slugify 4 | from importlib import import_module 5 | from unidecode import unidecode 6 | 7 | 8 | # Timezone support with fallback. 9 | try: 10 | from django.utils.timezone import now 11 | except ImportError: 12 | from datetime import datetime 13 | now = datetime.now 14 | 15 | 16 | def slugify(s): 17 | """ 18 | Translates unicode into closest possible ascii chars before 19 | slugifying. 20 | """ 21 | from future.builtins import str 22 | return django_slugify(unidecode(str(s))) 23 | 24 | 25 | def unique_slug(manager, slug_field, slug): 26 | """ 27 | Ensure slug is unique for the given manager, appending a digit 28 | if it isn't. 29 | """ 30 | i = 0 31 | while True: 32 | if i > 0: 33 | if i > 1: 34 | slug = slug.rsplit("-", 1)[0] 35 | slug = "%s-%s" % (slug, i) 36 | if not manager.filter(**{slug_field: slug}): 37 | break 38 | i += 1 39 | return slug 40 | 41 | 42 | def split_choices(choices_string): 43 | """ 44 | Convert a comma separated choices string to a list. 45 | """ 46 | return [x.strip() for x in choices_string.split(",") if x.strip()] 47 | 48 | 49 | def html5_field(name, base): 50 | """ 51 | Takes a Django form field class and returns a subclass of 52 | it with the given name as its input type. 53 | """ 54 | return type(str(""), (base,), {"input_type": name}) 55 | 56 | 57 | def import_attr(path): 58 | """ 59 | Given a a Python dotted path to a variable in a module, 60 | imports the module and returns the variable in it. 61 | """ 62 | module_path, attr_name = path.rsplit(".", 1) 63 | return getattr(import_module(module_path), attr_name) 64 | -------------------------------------------------------------------------------- /forms_builder/forms/templatetags/forms_builder_tags.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from future.builtins import str 3 | 4 | from django import template 5 | from django.template.loader import get_template 6 | 7 | from forms_builder.forms.forms import FormForForm 8 | from forms_builder.forms.models import Form, AbstractForm 9 | 10 | 11 | register = template.Library() 12 | 13 | 14 | class BuiltFormNode(template.Node): 15 | 16 | def __init__(self, name, value): 17 | self.name = name 18 | self.value = value 19 | 20 | def render(self, context): 21 | request = context["request"] 22 | user = getattr(request, "user", None) 23 | post = getattr(request, "POST", None) 24 | files = getattr(request, "FILES", None) 25 | if self.name != "form": 26 | lookup_value = template.Variable(self.value).resolve(context) 27 | try: 28 | form = Form.objects.get(**{str(self.name): lookup_value}) 29 | except Form.DoesNotExist: 30 | form = None 31 | else: 32 | form = template.Variable(self.value).resolve(context) 33 | if (not issubclass(form.__class__, AbstractForm) 34 | or not form.published(for_user=user)): 35 | return "" 36 | t = get_template("forms/includes/built_form.html") 37 | context["form"] = form 38 | form_args = (form, context, post or None, files or None) 39 | context["form_for_form"] = FormForForm(*form_args) 40 | return t.render(context) 41 | 42 | 43 | @register.tag 44 | def render_built_form(parser, token): 45 | """ 46 | render_build_form takes one argument in one of the following formats: 47 | 48 | {% render_build_form form_instance %} 49 | {% render_build_form form=form_instance %} 50 | {% render_build_form id=form_instance.id %} 51 | {% render_build_form slug=form_instance.slug %} 52 | 53 | """ 54 | try: 55 | _, arg = token.split_contents() 56 | if "=" not in arg: 57 | arg = "form=" + arg 58 | name, value = arg.split("=", 1) 59 | if name not in ("form", "id", "slug"): 60 | raise ValueError 61 | except ValueError: 62 | e = () 63 | raise template.TemplateSyntaxError(render_built_form.__doc__) 64 | return BuiltFormNode(name, value) 65 | -------------------------------------------------------------------------------- /forms_builder/forms/settings.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from django.conf import settings 4 | from django.core.exceptions import ImproperlyConfigured 5 | 6 | 7 | if not ("django.contrib.sites" in settings.INSTALLED_APPS): 8 | raise ImproperlyConfigured("django.contrib.sites is required") 9 | 10 | 11 | # The maximum allowed length for field values. 12 | FIELD_MAX_LENGTH = getattr(settings, "FORMS_BUILDER_FIELD_MAX_LENGTH", 2000) 13 | 14 | # The maximum allowed length for field labels. 15 | LABEL_MAX_LENGTH = getattr(settings, "FORMS_BUILDER_LABEL_MAX_LENGTH", 200) 16 | 17 | # Sequence of custom fields that will be added to the form field types. 18 | EXTRA_FIELDS = getattr(settings, "FORMS_BUILDER_EXTRA_FIELDS", ()) 19 | 20 | # Sequence of custom widgets that will add/update form fields widgets. 21 | EXTRA_WIDGETS = getattr(settings, "FORMS_BUILDER_EXTRA_WIDGETS", ()) 22 | 23 | # The absolute path where files will be uploaded to. 24 | UPLOAD_ROOT = getattr(settings, "FORMS_BUILDER_UPLOAD_ROOT", None) 25 | 26 | # Boolean controlling whether HTML5 form fields are used. 27 | USE_HTML5 = getattr(settings, "FORMS_BUILDER_USE_HTML5", True) 28 | 29 | # Boolean controlling whether forms are associated to Django's Sites framework. 30 | USE_SITES = getattr(settings, "FORMS_BUILDER_USE_SITES", 31 | hasattr(settings, "SITE_ID")) 32 | 33 | # Boolean controlling whether form slugs are editable in the admin. 34 | EDITABLE_SLUGS = getattr(settings, "FORMS_BUILDER_EDITABLE_SLUGS", False) 35 | 36 | # Char to start a quoted choice with. 37 | CHOICES_QUOTE = getattr(settings, "FORMS_BUILDER_CHOICES_QUOTE", "`") 38 | 39 | # Char to end a quoted choice with. 40 | CHOICES_UNQUOTE = getattr(settings, "FORMS_BUILDER_CHOICES_UNQUOTE", "`") 41 | 42 | # Char to use as a field delimiter when exporting form responses as CSV. 43 | CSV_DELIMITER = getattr(settings, "FORMS_BUILDER_CSV_DELIMITER", ",") 44 | 45 | # The maximum allowed length for field help text 46 | HELPTEXT_MAX_LENGTH = getattr(settings, "FORMS_BUILDER_HELPTEXT_MAX_LENGTH", 100) 47 | 48 | # The maximum allowed length for field choices 49 | CHOICES_MAX_LENGTH = getattr(settings, "FORMS_BUILDER_CHOICES_MAX_LENGTH", 1000) 50 | 51 | # Does sending emails fail silently or raise an exception. 52 | EMAIL_FAIL_SILENTLY = getattr(settings, "FORMS_BUILDER_EMAIL_FAIL_SILENTLY", 53 | settings.DEBUG) 54 | 55 | # Django SITE_ID - need a default since no longer provided in settings.py. 56 | SITE_ID = getattr(settings, "SITE_ID", 1) 57 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import sys 4 | from shutil import rmtree 5 | from setuptools import setup, find_packages 6 | 7 | 8 | exclude = ["forms_builder/example_project/dev.db", 9 | "forms_builder/example_project/local_settings.py"] 10 | exclude = dict([(e, None) for e in exclude]) 11 | for e in exclude: 12 | if e.endswith(".py"): 13 | try: 14 | os.remove("%sc" % e) 15 | except: 16 | pass 17 | try: 18 | with open(e, "r") as f: 19 | exclude[e] = (f.read(), os.stat(e)) 20 | os.remove(e) 21 | except Exception: 22 | pass 23 | 24 | if sys.argv[:2] == ["setup.py", "bdist_wheel"]: 25 | # Remove previous build dir when creating a wheel build, 26 | # since if files have been removed from the project, 27 | # they'll still be cached in the build dir and end up 28 | # as part of the build, which is unexpected. 29 | try: 30 | rmtree("build") 31 | except: 32 | pass 33 | 34 | try: 35 | setup( 36 | name = "django-forms-builder", 37 | version = __import__("forms_builder").__version__, 38 | author = "Stephen McDonald", 39 | author_email = "stephen.mc@gmail.com", 40 | description = ("A Django reusable app providing the ability for " 41 | "admin users to create their own forms and report " 42 | "on their collected data."), 43 | long_description = open("README.rst").read(), 44 | license = "BSD", 45 | url = "http://github.com/stephenmcd/django-forms-builder", 46 | zip_safe = False, 47 | include_package_data = True, 48 | packages = find_packages(), 49 | install_requires = [ 50 | "sphinx-me >= 0.1.2", 51 | "unidecode", 52 | "django-email-extras >= 0.2", 53 | "django >= 1.8, < 1.11", 54 | "future <= 0.15.0", 55 | ], 56 | classifiers = [ 57 | "Development Status :: 5 - Production/Stable", 58 | "Environment :: Web Environment", 59 | "Intended Audience :: Developers", 60 | "Operating System :: OS Independent", 61 | "Programming Language :: Python", 62 | "Programming Language :: Python :: 2.7", 63 | "Programming Language :: Python :: 3", 64 | "Programming Language :: Python :: 3.4", 65 | "Programming Language :: Python :: 3.5", 66 | "Framework :: Django", 67 | "Topic :: Internet :: WWW/HTTP :: Dynamic Content", 68 | "Topic :: Internet :: WWW/HTTP :: Site Management", 69 | ] 70 | ) 71 | finally: 72 | for e in exclude: 73 | if exclude[e] is not None: 74 | data, stat = exclude[e] 75 | try: 76 | with open(e, "w") as f: 77 | f.write(data) 78 | os.chown(e, stat.st_uid, stat.st_gid) 79 | os.chmod(e, stat.st_mode) 80 | except: 81 | pass 82 | -------------------------------------------------------------------------------- /forms_builder/example_project/settings.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import, unicode_literals 2 | 3 | import os, sys 4 | 5 | 6 | DEBUG = True 7 | SITE_ID = 1 8 | PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__)) 9 | PROJECT_DIRNAME = PROJECT_ROOT.split(os.sep)[-1] 10 | STATIC_URL = "/static/" 11 | STATIC_ROOT = os.path.join(PROJECT_ROOT, STATIC_URL.strip("/")) 12 | MEDIA_URL = STATIC_URL + "media/" 13 | MEDIA_ROOT = os.path.join(PROJECT_ROOT, *MEDIA_URL.strip("/").split("/")) 14 | ADMIN_MEDIA_PREFIX = STATIC_URL + "admin/" 15 | ROOT_URLCONF = "%s.urls" % PROJECT_DIRNAME 16 | TEMPLATE_DIRS = (os.path.join(PROJECT_ROOT, "templates"),) 17 | SECRET_KEY = "asdfa4wtW#$Gse4aGdfs" 18 | ADMINS = () 19 | 20 | 21 | MANAGERS = ADMINS 22 | if "test" not in sys.argv: 23 | LOGIN_URL = "/admin/" 24 | 25 | DATABASES = { 26 | 'default': { 27 | 'ENGINE': 'django.db.backends.sqlite3', 28 | 'NAME': 'dev.db', 29 | } 30 | } 31 | 32 | TEMPLATES = [ 33 | { 34 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 35 | 'DIRS': [ 36 | os.path.join(PROJECT_ROOT, "templates") 37 | ], 38 | 'APP_DIRS': True, 39 | 'OPTIONS': { 40 | 'context_processors': [ 41 | 'django.contrib.auth.context_processors.auth', 42 | 'django.template.context_processors.debug', 43 | 'django.template.context_processors.request', 44 | 'django.template.context_processors.i18n', 45 | 'django.template.context_processors.media', 46 | 'django.template.context_processors.static', 47 | 'django.template.context_processors.tz', 48 | 'django.contrib.messages.context_processors.messages', 49 | ], 50 | }, 51 | }, 52 | ] 53 | 54 | MIDDLEWARE_CLASSES = ( 55 | 'django.middleware.common.CommonMiddleware', 56 | 'django.contrib.sessions.middleware.SessionMiddleware', 57 | 'django.middleware.csrf.CsrfViewMiddleware', 58 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 59 | 'django.contrib.messages.middleware.MessageMiddleware', 60 | ) 61 | 62 | TEMPLATE_CONTEXT_PROCESSORS = ( 63 | "django.contrib.auth.context_processors.auth", 64 | "django.contrib.messages.context_processors.messages", 65 | "django.core.context_processors.debug", 66 | "django.core.context_processors.i18n", 67 | "django.core.context_processors.static", 68 | "django.core.context_processors.media", 69 | "django.core.context_processors.request", 70 | ) 71 | 72 | INSTALLED_APPS = ( 73 | 'django.contrib.admin', 74 | 'django.contrib.auth', 75 | 'django.contrib.contenttypes', 76 | 'django.contrib.sessions', 77 | 'django.contrib.sites', 78 | 'django.contrib.staticfiles', 79 | 'forms_builder.forms', 80 | ) 81 | 82 | FORMS_BUILDER_EXTRA_FIELDS = ( 83 | (100, "django.forms.BooleanField", "My cool checkbox"), 84 | ) 85 | 86 | try: 87 | from local_settings import * 88 | except ImportError: 89 | pass 90 | 91 | TEMPLATE_DEBUG = DEBUG 92 | -------------------------------------------------------------------------------- /forms_builder/forms/fields.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from django.core.exceptions import ImproperlyConfigured 4 | from django import forms 5 | from django.forms.extras import SelectDateWidget 6 | from django.utils.translation import ugettext_lazy as _ 7 | 8 | from forms_builder.forms.settings import USE_HTML5, EXTRA_FIELDS, EXTRA_WIDGETS 9 | from forms_builder.forms.utils import html5_field, import_attr 10 | 11 | 12 | # Constants for all available field types. 13 | TEXT = 1 14 | TEXTAREA = 2 15 | EMAIL = 3 16 | CHECKBOX = 4 17 | CHECKBOX_MULTIPLE = 5 18 | SELECT = 6 19 | SELECT_MULTIPLE = 7 20 | RADIO_MULTIPLE = 8 21 | FILE = 9 22 | DATE = 10 23 | DATE_TIME = 11 24 | HIDDEN = 12 25 | NUMBER = 13 26 | URL = 14 27 | DOB = 15 28 | 29 | # Names for all available field types. 30 | NAMES = ( 31 | (TEXT, _("Single line text")), 32 | (TEXTAREA, _("Multi line text")), 33 | (EMAIL, _("Email")), 34 | (NUMBER, _("Number")), 35 | (URL, _("URL")), 36 | (CHECKBOX, _("Check box")), 37 | (CHECKBOX_MULTIPLE, _("Check boxes")), 38 | (SELECT, _("Drop down")), 39 | (SELECT_MULTIPLE, _("Multi select")), 40 | (RADIO_MULTIPLE, _("Radio buttons")), 41 | (FILE, _("File upload")), 42 | (DATE, _("Date")), 43 | (DATE_TIME, _("Date/time")), 44 | (DOB, _("Date of birth")), 45 | (HIDDEN, _("Hidden")), 46 | ) 47 | 48 | # Field classes for all available field types. 49 | CLASSES = { 50 | TEXT: forms.CharField, 51 | TEXTAREA: forms.CharField, 52 | EMAIL: forms.EmailField, 53 | CHECKBOX: forms.BooleanField, 54 | CHECKBOX_MULTIPLE: forms.MultipleChoiceField, 55 | SELECT: forms.ChoiceField, 56 | SELECT_MULTIPLE: forms.MultipleChoiceField, 57 | RADIO_MULTIPLE: forms.ChoiceField, 58 | FILE: forms.FileField, 59 | DATE: forms.DateField, 60 | DATE_TIME: forms.DateTimeField, 61 | DOB: forms.DateField, 62 | HIDDEN: forms.CharField, 63 | NUMBER: forms.FloatField, 64 | URL: forms.URLField, 65 | } 66 | 67 | # Widgets for field types where a specialised widget is required. 68 | WIDGETS = { 69 | TEXTAREA: forms.Textarea, 70 | CHECKBOX_MULTIPLE: forms.CheckboxSelectMultiple, 71 | RADIO_MULTIPLE: forms.RadioSelect, 72 | DATE: SelectDateWidget, 73 | DOB: SelectDateWidget, 74 | HIDDEN: forms.HiddenInput, 75 | } 76 | 77 | # Some helper groupings of field types. 78 | CHOICES = (CHECKBOX, SELECT, RADIO_MULTIPLE) 79 | DATES = (DATE, DATE_TIME, DOB) 80 | MULTIPLE = (CHECKBOX_MULTIPLE, SELECT_MULTIPLE) 81 | 82 | # HTML5 Widgets 83 | if USE_HTML5: 84 | WIDGETS.update({ 85 | DATE: html5_field("date", forms.DateInput), 86 | DATE_TIME: html5_field("datetime", forms.DateTimeInput), 87 | DOB: html5_field("date", forms.DateInput), 88 | EMAIL: html5_field("email", forms.TextInput), 89 | NUMBER: html5_field("number", forms.TextInput), 90 | URL: html5_field("url", forms.TextInput), 91 | }) 92 | 93 | # Add any custom fields defined. 94 | for field_id, field_path, field_name in EXTRA_FIELDS: 95 | if field_id in CLASSES: 96 | err = "ID %s for field %s in FORMS_EXTRA_FIELDS already exists" 97 | raise ImproperlyConfigured(err % (field_id, field_name)) 98 | CLASSES[field_id] = import_attr(field_path) 99 | NAMES += ((field_id, _(field_name)),) 100 | 101 | # Add/update custom widgets. 102 | for field_id, widget_path in EXTRA_WIDGETS: 103 | if field_id not in CLASSES: 104 | err = "ID %s in FORMS_EXTRA_WIDGETS does not match a field" 105 | raise ImproperlyConfigured(err % field_id) 106 | WIDGETS[field_id] = import_attr(widget_path) 107 | -------------------------------------------------------------------------------- /forms_builder/forms/views.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | import json 4 | 5 | from django.conf import settings 6 | from django.contrib.auth import REDIRECT_FIELD_NAME 7 | from django.core.urlresolvers import reverse 8 | from django.http import HttpResponse, HttpResponseBadRequest 9 | from django.shortcuts import get_object_or_404, redirect, render_to_response 10 | from django.template import RequestContext 11 | from django.utils.http import urlquote 12 | from django.views.generic.base import TemplateView 13 | from email_extras.utils import send_mail_template 14 | 15 | from forms_builder.forms.forms import FormForForm 16 | from forms_builder.forms.models import Form 17 | from forms_builder.forms.settings import EMAIL_FAIL_SILENTLY 18 | from forms_builder.forms.signals import form_invalid, form_valid 19 | from forms_builder.forms.utils import split_choices 20 | 21 | 22 | class FormDetail(TemplateView): 23 | 24 | template_name = "forms/form_detail.html" 25 | 26 | def get_context_data(self, **kwargs): 27 | context = super(FormDetail, self).get_context_data(**kwargs) 28 | published = Form.objects.published(for_user=self.request.user) 29 | context["form"] = get_object_or_404(published, slug=kwargs["slug"]) 30 | return context 31 | 32 | def get(self, request, *args, **kwargs): 33 | context = self.get_context_data(**kwargs) 34 | login_required = context["form"].login_required 35 | if login_required and not request.user.is_authenticated(): 36 | path = urlquote(request.get_full_path()) 37 | bits = (settings.LOGIN_URL, REDIRECT_FIELD_NAME, path) 38 | return redirect("%s?%s=%s" % bits) 39 | return self.render_to_response(context) 40 | 41 | def post(self, request, *args, **kwargs): 42 | published = Form.objects.published(for_user=request.user) 43 | form = get_object_or_404(published, slug=kwargs["slug"]) 44 | form_for_form = FormForForm(form, RequestContext(request), 45 | request.POST or None, 46 | request.FILES or None) 47 | if not form_for_form.is_valid(): 48 | form_invalid.send(sender=request, form=form_for_form) 49 | else: 50 | # Attachments read must occur before model save, 51 | # or seek() will fail on large uploads. 52 | attachments = [] 53 | for f in form_for_form.files.values(): 54 | f.seek(0) 55 | attachments.append((f.name, f.read())) 56 | entry = form_for_form.save() 57 | form_valid.send(sender=request, form=form_for_form, entry=entry) 58 | self.send_emails(request, form_for_form, form, entry, attachments) 59 | if not self.request.is_ajax(): 60 | return redirect(form.redirect_url or 61 | reverse("form_sent", kwargs={"slug": form.slug})) 62 | context = {"form": form, "form_for_form": form_for_form} 63 | return self.render_to_response(context) 64 | 65 | def render_to_response(self, context, **kwargs): 66 | if self.request.method == "POST" and self.request.is_ajax(): 67 | json_context = json.dumps({ 68 | "errors": context["form_for_form"].errors, 69 | "form": context["form_for_form"].as_p(), 70 | "message": context["form"].response, 71 | }) 72 | if context["form_for_form"].errors: 73 | return HttpResponseBadRequest(json_context, 74 | content_type="application/json") 75 | return HttpResponse(json_context, content_type="application/json") 76 | return super(FormDetail, self).render_to_response(context, **kwargs) 77 | 78 | def send_emails(self, request, form_for_form, form, entry, attachments): 79 | subject = form.email_subject 80 | if not subject: 81 | subject = "%s - %s" % (form.title, entry.entry_time) 82 | fields = [] 83 | for (k, v) in form_for_form.fields.items(): 84 | value = form_for_form.cleaned_data[k] 85 | if isinstance(value, list): 86 | value = ", ".join([i.strip() for i in value]) 87 | fields.append((v.label, value)) 88 | context = { 89 | "fields": fields, 90 | "message": form.email_message, 91 | "request": request, 92 | } 93 | email_from = form.email_from or settings.DEFAULT_FROM_EMAIL 94 | email_to = form_for_form.email_to() 95 | if email_to and form.send_email: 96 | send_mail_template(subject, "form_response", email_from, 97 | email_to, context=context, 98 | fail_silently=EMAIL_FAIL_SILENTLY) 99 | headers = None 100 | if email_to: 101 | headers = {"Reply-To": email_to} 102 | email_copies = split_choices(form.email_copies) 103 | if email_copies: 104 | send_mail_template(subject, "form_response_copies", email_from, 105 | email_copies, context=context, 106 | attachments=attachments, 107 | fail_silently=EMAIL_FAIL_SILENTLY, 108 | headers=headers) 109 | 110 | form_detail = FormDetail.as_view() 111 | 112 | 113 | def form_sent(request, slug, template="forms/form_sent.html"): 114 | """ 115 | Show the response message. 116 | """ 117 | published = Form.objects.published(for_user=request.user) 118 | context = {"form": get_object_or_404(published, slug=slug)} 119 | return render_to_response(template, context, RequestContext(request)) 120 | -------------------------------------------------------------------------------- /forms_builder/forms/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | from forms_builder.forms import settings 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('sites', '0001_initial'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Field', 18 | fields=[ 19 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 20 | ('label', models.CharField(max_length=200, verbose_name='Label')), 21 | ('slug', models.SlugField(default='', max_length=100, verbose_name='Slug', blank=True)), 22 | ('field_type', models.IntegerField(verbose_name='Type', choices=[(1, 'Single line text'), (2, 'Multi line text'), (3, 'Email'), (13, 'Number'), (14, 'URL'), (4, 'Check box'), (5, 'Check boxes'), (6, 'Drop down'), (7, 'Multi select'), (8, 'Radio buttons'), (9, 'File upload'), (10, 'Date'), (11, 'Date/time'), (15, 'Date of birth'), (12, 'Hidden')])), 23 | ('required', models.BooleanField(default=True, verbose_name='Required')), 24 | ('visible', models.BooleanField(default=True, verbose_name='Visible')), 25 | ('choices', models.CharField(help_text='Comma separated options where applicable. If an option itself contains commas, surround the option starting with the `character and ending with the ` character.', max_length=1000, verbose_name='Choices', blank=True)), 26 | ('default', models.CharField(max_length=2000, verbose_name='Default value', blank=True)), 27 | ('placeholder_text', models.CharField(max_length=100, null=True, verbose_name='Placeholder Text', blank=True)), 28 | ('help_text', models.CharField(max_length=100, verbose_name='Help text', blank=True)), 29 | ('order', models.IntegerField(null=True, verbose_name='Order', blank=True)), 30 | ], 31 | options={ 32 | 'ordering': ('order',), 33 | 'abstract': False, 34 | 'verbose_name': 'Field', 35 | 'verbose_name_plural': 'Fields', 36 | }, 37 | bases=(models.Model,), 38 | ), 39 | migrations.CreateModel( 40 | name='FieldEntry', 41 | fields=[ 42 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 43 | ('field_id', models.IntegerField()), 44 | ('value', models.CharField(max_length=2000, null=True)), 45 | ], 46 | options={ 47 | 'abstract': False, 48 | 'verbose_name': 'Form field entry', 49 | 'verbose_name_plural': 'Form field entries', 50 | }, 51 | bases=(models.Model,), 52 | ), 53 | migrations.CreateModel( 54 | name='Form', 55 | fields=[ 56 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 57 | ('title', models.CharField(max_length=50, verbose_name='Title')), 58 | ('slug', models.SlugField(verbose_name='Slug', unique=True, max_length=100, editable=False)), 59 | ('intro', models.TextField(verbose_name='Intro', blank=True)), 60 | ('button_text', models.CharField(default='Submit', max_length=50, verbose_name='Button text')), 61 | ('response', models.TextField(verbose_name='Response', blank=True)), 62 | ('redirect_url', models.CharField(help_text='An alternate URL to redirect to after form submission', max_length=200, null=True, verbose_name='Redirect url', blank=True)), 63 | ('status', models.IntegerField(default=2, verbose_name='Status', choices=[(1, 'Draft'), (2, 'Published')])), 64 | ('publish_date', models.DateTimeField(help_text="With published selected, won't be shown until this time", null=True, verbose_name='Published from', blank=True)), 65 | ('expiry_date', models.DateTimeField(help_text="With published selected, won't be shown after this time", null=True, verbose_name='Expires on', blank=True)), 66 | ('login_required', models.BooleanField(default=False, help_text='If checked, only logged in users can view the form', verbose_name='Login required')), 67 | ('send_email', models.BooleanField(default=True, help_text='If checked, the person entering the form will be sent an email', verbose_name='Send email')), 68 | ('email_from', models.EmailField(help_text='The address the email will be sent from', max_length=75, verbose_name='From address', blank=True)), 69 | ('email_copies', models.CharField(help_text='One or more email addresses, separated by commas', max_length=200, verbose_name='Send copies to', blank=True)), 70 | ('email_subject', models.CharField(max_length=200, verbose_name='Subject', blank=True)), 71 | ('email_message', models.TextField(verbose_name='Message', blank=True)), 72 | ('sites', models.ManyToManyField(default=[settings.SITE_ID], related_name='forms_form_forms', to='sites.Site')), 73 | ], 74 | options={ 75 | 'abstract': False, 76 | 'verbose_name': 'Form', 77 | 'verbose_name_plural': 'Forms', 78 | }, 79 | bases=(models.Model,), 80 | ), 81 | migrations.CreateModel( 82 | name='FormEntry', 83 | fields=[ 84 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 85 | ('entry_time', models.DateTimeField(verbose_name='Date/time')), 86 | ('form', models.ForeignKey(related_name='entries', to='forms.Form')), 87 | ], 88 | options={ 89 | 'abstract': False, 90 | 'verbose_name': 'Form entry', 91 | 'verbose_name_plural': 'Form entries', 92 | }, 93 | bases=(models.Model,), 94 | ), 95 | migrations.AddField( 96 | model_name='fieldentry', 97 | name='entry', 98 | field=models.ForeignKey(related_name='fields', to='forms.FormEntry'), 99 | preserve_default=True, 100 | ), 101 | migrations.AddField( 102 | model_name='field', 103 | name='form', 104 | field=models.ForeignKey(related_name='fields', to='forms.Form'), 105 | preserve_default=True, 106 | ), 107 | ] 108 | -------------------------------------------------------------------------------- /forms_builder/forms/locale/de/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | "Project-Id-Version: django-forms-builder\n" 2 | "Report-Msgid-Bugs-To: \n" 3 | "POT-Creation-Date: 2013-09-04 15:42+0200\n" 4 | "PO-Revision-Date: Wed Sep 04 2013 16:25:17 GMT+0200\n" 5 | "Last-Translator: Sven Rojek \n" 6 | "Language-Team: Sven Rojek \n" 7 | "Language: German\n" 8 | "Plural-Forms: nplurals=2; plural=(n != 1)\n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | 13 | msgid "Email" 14 | msgstr "E-Mail" 15 | 16 | #, fuzzy 17 | msgid "Slug" 18 | msgstr "Kurzform" 19 | 20 | msgid "Sites" 21 | msgstr "Seiten" 22 | 23 | msgid "1 entry deleted" 24 | msgid_plural "%(count)s entries deleted" 25 | msgstr[0] "1 Eintrag gelöscht" 26 | msgstr[1] "%(count)s Einträge gelöscht" 27 | 28 | msgid "View Entries" 29 | msgstr "Einträge anzeigen" 30 | 31 | msgid "Single line text" 32 | msgstr "Einzeiliges Eingabefeld" 33 | 34 | msgid "Multi line text" 35 | msgstr "Mehrzeiliger Eingabebereich" 36 | 37 | msgid "Number" 38 | msgstr "Nummer" 39 | 40 | msgid "URL" 41 | msgstr "URL" 42 | 43 | msgid "Check box" 44 | msgstr "Checkbox" 45 | 46 | msgid "Check boxes" 47 | msgstr "Checkboxen" 48 | 49 | msgid "Drop down" 50 | msgstr "Dropdown" 51 | 52 | msgid "Multi select" 53 | msgstr "Auswahllisten mit Mehrfachauswahl" 54 | 55 | msgid "Radio buttons" 56 | msgstr "Radio-Buttons" 57 | 58 | msgid "File upload" 59 | msgstr "Datei-Upload" 60 | 61 | msgid "Date" 62 | msgstr "Datum" 63 | 64 | msgid "Date/time" 65 | msgstr "Datum/Uhrzeit" 66 | 67 | msgid "Date of birth" 68 | msgstr "Geburtsdatum" 69 | 70 | msgid "Hidden" 71 | msgstr "Versteckt" 72 | 73 | msgid "Nothing" 74 | msgstr "Nichts" 75 | 76 | msgid "Contains" 77 | msgstr "Enthält" 78 | 79 | msgid "Doesn't contain" 80 | msgstr "Enthält nicht" 81 | 82 | msgid "Equals" 83 | msgstr "Gleich" 84 | 85 | msgid "Doesn't equal" 86 | msgstr "Ungleich" 87 | 88 | msgid "Equals any" 89 | msgstr "Gleich" 90 | 91 | msgid "Doesn't equal any" 92 | msgstr "Ungleich" 93 | 94 | msgid "Contains any" 95 | msgstr "Enthält" 96 | 97 | msgid "Contains all" 98 | msgstr "Enthält alle" 99 | 100 | msgid "Doesn't contain any" 101 | msgstr "Enthält nicht" 102 | 103 | msgid "Doesn't contain all" 104 | msgstr "Enthält keines" 105 | 106 | msgid "Is between" 107 | msgstr "Zwischen" 108 | 109 | msgid "Checked" 110 | msgstr "Ausgewählt" 111 | 112 | msgid "Not checked" 113 | msgstr "Nicht ausgewählt" 114 | 115 | msgid "and" 116 | msgstr "und" 117 | 118 | msgid "Draft" 119 | msgstr "Entwurf" 120 | 121 | msgid "Published" 122 | msgstr "Veröffentlicht" 123 | 124 | msgid "Title" 125 | msgstr "Titel" 126 | 127 | msgid "Intro" 128 | msgstr "Einführung" 129 | 130 | msgid "Button text" 131 | msgstr "Button-Text" 132 | 133 | msgid "Submit" 134 | msgstr "Senden" 135 | 136 | msgid "Response" 137 | msgstr "Antwort" 138 | 139 | msgid "Status" 140 | msgstr "Status" 141 | 142 | msgid "Published from" 143 | msgstr "Veröffentlicht von" 144 | 145 | msgid "With published selected, won't be shown until this time" 146 | msgstr "Mit Veröffentlicht ausgewählt, wird bis zu diesem Zeitpunkt nicht angezeigt" 147 | 148 | msgid "Expires on" 149 | msgstr "Gültig bis" 150 | 151 | msgid "With published selected, won't be shown after this time" 152 | msgstr "Mit Veröffentlicht ausgewählt, wird nach diesem Zeitpunkt nicht mehr angezeigt" 153 | 154 | msgid "Login required" 155 | msgstr "Anmeldung erforderlich" 156 | 157 | msgid "If checked, only logged in users can view the form" 158 | msgstr "Wenn ausgewählt, können nur eingeloggte Nutzer das Formular sehen." 159 | 160 | msgid "Send email" 161 | msgstr "E-Mail versenden" 162 | 163 | msgid "If checked, the person entering the form will be sent an email" 164 | msgstr "Wenn ausgewählt, wird an die ausfüllende Person eine E-Mail gesendet" 165 | 166 | msgid "From address" 167 | msgstr "Absenderadresse" 168 | 169 | msgid "The address the email will be sent from" 170 | msgstr "Die E-Mail-Adresse von welcher versendet wird" 171 | 172 | msgid "Send copies to" 173 | msgstr "Sende Kopien an" 174 | 175 | msgid "One or more email addresses, separated by commas" 176 | msgstr "Eine oder mehrere E-Mail Adressen, getrennt durch Kommata" 177 | 178 | msgid "Subject" 179 | msgstr "Betreff" 180 | 181 | msgid "Message" 182 | msgstr "Nachricht" 183 | 184 | msgid "Form" 185 | msgstr "Formular" 186 | 187 | msgid "Forms" 188 | msgstr "Formulare" 189 | 190 | msgid "View form on site" 191 | msgstr "Formular auf der Seite anzeigen" 192 | 193 | msgid "Filter entries" 194 | msgstr "Filter Einträge" 195 | 196 | msgid "View all entries" 197 | msgstr "Zeige alle Einträge" 198 | 199 | msgid "Export all entries" 200 | msgstr "Alle Einträge exportieren" 201 | 202 | msgid "Placeholder Text" 203 | msgstr "Platzhaltertext" 204 | 205 | msgid "Label" 206 | msgstr "Beschriftung" 207 | 208 | msgid "Type" 209 | msgstr "Typ" 210 | 211 | msgid "Required" 212 | msgstr "Benötigt" 213 | 214 | msgid "Visible" 215 | msgstr "Sichtbar" 216 | 217 | msgid "Choices" 218 | msgstr "Auswahl" 219 | 220 | msgid "Default value" 221 | msgstr "Standardwert" 222 | 223 | msgid "Help text" 224 | msgstr "Hilfetext" 225 | 226 | msgid "Field" 227 | msgstr "Feld" 228 | 229 | msgid "Fields" 230 | msgstr "Felder" 231 | 232 | msgid "Form entry" 233 | msgstr "Formular Eintrag" 234 | 235 | msgid "Form entries" 236 | msgstr "Formular Einträge" 237 | 238 | msgid "Form field entry" 239 | msgstr "Formularfeld Eintrag" 240 | 241 | msgid "Form field entries" 242 | msgstr "Formularfeld Einträge" 243 | 244 | msgid "Order" 245 | msgstr "Reihenfolge" 246 | 247 | msgid "View entries" 248 | msgstr "Zeige Einträge" 249 | 250 | msgid "History" 251 | msgstr "Geschichte" 252 | 253 | msgid "View on site" 254 | msgstr "Auf Seite anzeigen" 255 | 256 | msgid "No entries selected" 257 | msgstr "Keine Einträge ausgewählt" 258 | 259 | msgid "Delete selected entries?" 260 | msgstr "Ausgewählte Einträge löschen?" 261 | 262 | msgid "Home" 263 | msgstr "Home" 264 | 265 | msgid "Include" 266 | msgstr "Enthalten" 267 | 268 | msgid "Filter by" 269 | msgstr "Filter nach" 270 | 271 | msgid "All" 272 | msgstr "Alle" 273 | 274 | msgid "Back to form" 275 | msgstr "Zurück zum Formular" 276 | 277 | msgid "Export CSV" 278 | msgstr "Als CSV exportieren" 279 | 280 | msgid "Export XLS" 281 | msgstr "Als XLS exportieren" 282 | 283 | msgid "Entries" 284 | msgstr "Einträge" 285 | 286 | msgid "Delete selected" 287 | msgstr "Lösche ausgewählte" 288 | 289 | msgid "No entries to display" 290 | msgstr "Keine Einträge zum Anzeigen" 291 | -------------------------------------------------------------------------------- /forms_builder/forms/templates/admin/forms/entries.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/base_site.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block extrahead %} 6 | {{ block.super }} 7 | 28 | 29 | 70 | {% endblock %} 71 | 72 | {% block breadcrumbs %} 73 | 80 | {% endblock %} 81 | 82 | {% block content %} 83 |
    84 |
    85 | {% csrf_token %} 86 | 87 | 88 | 89 | 90 | 91 | 92 | {% for include_field, filter_field, filter_option_fields in entries_form %} 93 | 94 | 95 | 96 | 97 | 104 | 105 | {% endfor %} 106 | 107 | 110 | 113 | 114 | 115 |
    {% trans "Field" %}{% trans "Include" %}{% trans "Filter by" %}
    {{ include_field.label_tag }}{{ include_field }}{{ filter_field }} 98 |
    99 | {% for option_field in filter_option_fields %} 100 | {{ option_field.label_tag }} {{ option_field }} 101 | {% endfor %} 102 |
    103 |
    108 | 109 | 111 | 112 |  
    116 | 117 | 118 | 119 | {% if xlwt_installed %} 120 | 121 | {% endif %} 122 | {% if submitted %} 123 |
    124 |

    {% trans "Entries" %}

    125 | {% for row in entries_form.rows %} 126 | {% if forloop.first %} 127 | 128 | 129 | {% if can_delete_entries %} 130 | 131 | {% endif %} 132 | {% for column in entries_form.columns %} 133 | {{ column }} 134 | {% endfor %} 135 | 136 | {% endif %} 137 | 138 | {% if can_delete_entries %} 139 | 142 | {% endif %} 143 | {% for field in row %} 144 | {% if not forloop.first %} 145 | {{ field }} 146 | {% endif %} 147 | {% endfor %} 148 | 149 | {% if forloop.last %} 150 |
    140 | 141 |
    151 | 155 | 156 | {% if can_delete_entries %} 157 | 158 | 159 | {% endif %} 160 | {% endif %} 161 | {% empty %} 162 |

    {% trans "No entries to display" %}

    163 | {% endfor %} 164 | {% endif %} 165 |
    166 |
    167 | {% endblock %} 168 | -------------------------------------------------------------------------------- /forms_builder/forms/tests.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from django.conf import settings 4 | from django.contrib.auth.models import User, AnonymousUser 5 | from django.contrib.sites.models import Site 6 | from django.db import IntegrityError 7 | from django.http import HttpResponseRedirect 8 | from django.template import Context, RequestContext, Template 9 | from django.test import TestCase 10 | 11 | from forms_builder.forms.fields import NAMES, FILE 12 | from forms_builder.forms.forms import FormForForm 13 | from forms_builder.forms.models import (Form, Field, 14 | STATUS_DRAFT, STATUS_PUBLISHED) 15 | from forms_builder.forms.settings import USE_SITES 16 | from forms_builder.forms.signals import form_invalid, form_valid 17 | 18 | 19 | class Tests(TestCase): 20 | 21 | def setUp(self): 22 | self._site = Site.objects.get_current() 23 | 24 | def test_form_fields(self): 25 | """ 26 | Simple 200 status check against rendering and posting to forms with 27 | both optional and required fields. 28 | """ 29 | for required in (True, False): 30 | form = Form.objects.create(title="Test", status=STATUS_PUBLISHED) 31 | if USE_SITES: 32 | form.sites.add(self._site) 33 | form.save() 34 | for (field, _) in NAMES: 35 | form.fields.create(label=field, field_type=field, 36 | required=required, visible=True) 37 | response = self.client.get(form.get_absolute_url()) 38 | self.assertEqual(response.status_code, 200) 39 | fields = form.fields.visible() 40 | data = dict([(f.slug, "test") for f in fields]) 41 | response = self.client.post(form.get_absolute_url(), data=data) 42 | self.assertEqual(response.status_code, 200) 43 | 44 | def test_draft_form(self): 45 | """ 46 | Test that a form with draft status is only visible to staff. 47 | """ 48 | settings.DEBUG = True # Don't depend on having a 404 template. 49 | username = "test" 50 | password = "test" 51 | User.objects.create_superuser(username, "", password) 52 | self.client.logout() 53 | draft = Form.objects.create(title="Draft", status=STATUS_DRAFT) 54 | if USE_SITES: 55 | draft.sites.add(self._site) 56 | draft.save() 57 | response = self.client.get(draft.get_absolute_url()) 58 | self.assertEqual(response.status_code, 404) 59 | self.client.login(username=username, password=password) 60 | response = self.client.get(draft.get_absolute_url()) 61 | self.assertEqual(response.status_code, 200) 62 | 63 | def test_form_signals(self): 64 | """ 65 | Test that each of the signals are sent. 66 | """ 67 | events = ["valid", "invalid"] 68 | invalid = lambda **kwargs: events.remove("invalid") 69 | form_invalid.connect(invalid) 70 | valid = lambda **kwargs: events.remove("valid") 71 | form_valid.connect(valid) 72 | form = Form.objects.create(title="Signals", status=STATUS_PUBLISHED) 73 | if USE_SITES: 74 | form.sites.add(self._site) 75 | form.save() 76 | form.fields.create(label="field", field_type=NAMES[0][0], 77 | required=True, visible=True) 78 | self.client.post(form.get_absolute_url(), data={}) 79 | data = {form.fields.visible()[0].slug: "test"} 80 | self.client.post(form.get_absolute_url(), data=data) 81 | self.assertEqual(len(events), 0) 82 | 83 | def test_tag(self): 84 | """ 85 | Test that the different formats for the ``render_built_form`` 86 | tag all work. 87 | """ 88 | form = Form.objects.create(title="Tags", status=STATUS_PUBLISHED) 89 | request = type(str(""), (), {"META": {}, "user": AnonymousUser()})() 90 | context = RequestContext(request, {"form": form}) 91 | template = "{%% load forms_builder_tags %%}{%% render_built_form %s %%}" 92 | formats = ("form", "form=form", "id=form.id", "slug=form.slug") 93 | for format in formats: 94 | t = Template(template % format).render(context) 95 | self.assertTrue(form.get_absolute_url(), t) 96 | 97 | def test_optional_filefield(self): 98 | form = Form.objects.create(title="Test", status=STATUS_PUBLISHED) 99 | if USE_SITES: 100 | form.sites.add(self._site) 101 | form.save() 102 | form.fields.create(label="file field", 103 | field_type=FILE, 104 | required=False, 105 | visible=True) 106 | fields = form.fields.visible() 107 | data = {'field_%s' % fields[0].id: ''} 108 | context = Context({}) 109 | form_for_form = FormForForm(form, context, data=data) 110 | # Should not raise IntegrityError: forms_fieldentry.value 111 | # may not be NULL 112 | form_for_form.save() 113 | 114 | 115 | def test_field_validate_slug_names(self): 116 | form = Form.objects.create(title="Test") 117 | field = Field(form=form, 118 | label="First name", field_type=NAMES[0][0]) 119 | field.save() 120 | self.assertEqual(field.slug, 'first_name') 121 | 122 | field_2 = Field(form=form, 123 | label="First name", field_type=NAMES[0][0]) 124 | try: 125 | field_2.save() 126 | except IntegrityError: 127 | self.fail("Slugs were not auto-unique") 128 | 129 | def test_field_default_ordering(self): 130 | form = Form.objects.create(title="Test") 131 | form.fields.create(label="second field", 132 | field_type=NAMES[0][0], order=2) 133 | f1 = form.fields.create(label="first field", 134 | field_type=NAMES[0][0], order=1) 135 | self.assertEqual(form.fields.all()[0], f1) 136 | 137 | def test_form_errors(self): 138 | from future.builtins import str 139 | form = Form.objects.create(title="Test") 140 | if USE_SITES: 141 | form.sites.add(self._site) 142 | form.save() 143 | form.fields.create(label="field", field_type=NAMES[0][0], 144 | required=True, visible=True) 145 | response = self.client.post(form.get_absolute_url(), {"foo": "bar"}) 146 | self.assertTrue("This field is required" in str(response.content)) 147 | 148 | def test_form_redirect(self): 149 | redirect_url = 'http://example.com/foo' 150 | form = Form.objects.create(title='Test', redirect_url=redirect_url) 151 | if USE_SITES: 152 | form.sites.add(self._site) 153 | form.save() 154 | form.fields.create(label='field', field_type=NAMES[3][0], 155 | required=True, visible=True) 156 | form_absolute_url = form.get_absolute_url() 157 | response = self.client.post(form_absolute_url, {'field': '0'}) 158 | self.assertEqual(response["location"], redirect_url) 159 | response = self.client.post(form_absolute_url, {'field': 'bar'}) 160 | self.assertFalse(isinstance(response, HttpResponseRedirect)) 161 | -------------------------------------------------------------------------------- /forms_builder/forms/locale/nb/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Norwegian translation for stephenmcd's django-forms-builder 2 | # This file is distributed under the same license as the PACKAGE package. 3 | # Sindre Sorhus , 2011. 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: django-forms-builder\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "POT-Creation-Date: 2011-10-12 14:14+0200\n" 10 | "PO-Revision-Date: 2011-10-12 14:38+0100\n" 11 | "Last-Translator: Sindre Sorhus \n" 12 | "Language-Team: \n" 13 | "Language: \n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "Plural-Forms: nplurals=2; plural=(n != 1)\n" 18 | "X-Poedit-Language: Norwegian Bokmal\n" 19 | "X-Poedit-Country: NORWAY\n" 20 | "X-Poedit-SourceCharset: utf-8\n" 21 | 22 | #: admin.py:28 23 | #: fields.py:27 24 | msgid "Email" 25 | msgstr "E-post" 26 | 27 | #: admin.py:32 28 | msgid "Sites" 29 | msgstr "Sider" 30 | 31 | #: admin.py:114 32 | #, python-format 33 | msgid "1 entry deleted" 34 | msgid_plural "%(count)s entries deleted" 35 | msgstr[0] "Slettet ett innlegg" 36 | msgstr[1] "Slettet %(count)s innlegg" 37 | 38 | #: admin.py:119 39 | msgid "View Entries" 40 | msgstr "Vis innlegg" 41 | 42 | #: fields.py:25 43 | msgid "Single line text" 44 | msgstr "Tekstfelt" 45 | 46 | #: fields.py:26 47 | msgid "Multi line text" 48 | msgstr "Tekstområde" 49 | 50 | #: fields.py:28 51 | msgid "Check box" 52 | msgstr "Avmerkingsboks" 53 | 54 | #: fields.py:29 55 | msgid "Check boxes" 56 | msgstr "Avmerkingsbokser" 57 | 58 | #: fields.py:30 59 | msgid "Drop down" 60 | msgstr "Rullegardinmeny" 61 | 62 | #: fields.py:31 63 | msgid "Multi select" 64 | msgstr "Flervalgmeny" 65 | 66 | #: fields.py:32 67 | msgid "Radio buttons" 68 | msgstr "Radioknapp" 69 | 70 | #: fields.py:33 71 | msgid "File upload" 72 | msgstr "Filopplasting" 73 | 74 | #: fields.py:34 75 | msgid "Date" 76 | msgstr "Dato" 77 | 78 | #: fields.py:35 79 | #: models.py:210 80 | msgid "Date/time" 81 | msgstr "Dato/tid" 82 | 83 | #: fields.py:36 84 | msgid "Hidden" 85 | msgstr "Skjult" 86 | 87 | #: forms.py:27 88 | #: forms.py:35 89 | #: forms.py:41 90 | msgid "Nothing" 91 | msgstr "Ingenting" 92 | 93 | #: forms.py:28 94 | msgid "Contains" 95 | msgstr "Inneholder" 96 | 97 | #: forms.py:29 98 | msgid "Doesn't contain" 99 | msgstr "Inneholder ikke" 100 | 101 | #: forms.py:30 102 | #: forms.py:36 103 | msgid "Equals" 104 | msgstr "Er lik" 105 | 106 | #: forms.py:31 107 | #: forms.py:37 108 | msgid "Doesn't equal" 109 | msgstr "Er ikke lik" 110 | 111 | #: forms.py:42 112 | msgid "Is between" 113 | msgstr "Er mellom" 114 | 115 | #: forms.py:165 116 | msgid "Checked" 117 | msgstr "Valgt" 118 | 119 | #: forms.py:165 120 | msgid "Not checked" 121 | msgstr "Ikke valgt" 122 | 123 | #: forms.py:179 124 | #: forms.py:194 125 | msgid "and" 126 | msgstr "og" 127 | 128 | #: models.py:54 129 | msgid "Title" 130 | msgstr "Tittel" 131 | 132 | #: models.py:56 133 | msgid "Intro" 134 | msgstr "Intro" 135 | 136 | #: models.py:57 137 | msgid "Button text" 138 | msgstr "Knapp tekst" 139 | 140 | #: models.py:58 141 | msgid "Submit" 142 | msgstr "Send" 143 | 144 | #: models.py:59 145 | msgid "Response" 146 | msgstr "Respons" 147 | 148 | #: models.py:60 149 | msgid "Status" 150 | msgstr "Status" 151 | 152 | #: models.py:62 153 | msgid "Published from" 154 | msgstr "Publisert fra" 155 | 156 | #: models.py:63 157 | msgid "With published selected, won't be shown until this time" 158 | msgstr "Vil ikke bli vist før denne tiden hvis publisert er valgt" 159 | 160 | #: models.py:65 161 | msgid "Expires on" 162 | msgstr "Utløper" 163 | 164 | #: models.py:66 165 | msgid "With published selected, won't be shown after this time" 166 | msgstr "Vil ikke bli vist etter denne tiden hvis publisert er valgt" 167 | 168 | #: models.py:68 169 | msgid "Login required" 170 | msgstr "Krever innlogging" 171 | 172 | #: models.py:69 173 | msgid "If checked, only logged in users can view the form" 174 | msgstr "Bare innloggede brukere se skjemaet hvis valgt" 175 | 176 | #: models.py:70 177 | msgid "Send email" 178 | msgstr "Send e-post" 179 | 180 | #: models.py:71 181 | msgid "If checked, the person entering the form will be sent an email" 182 | msgstr "Innsenderen vil bli sendt en e-post hvis valgt" 183 | 184 | #: models.py:72 185 | msgid "From address" 186 | msgstr "Fra adresse" 187 | 188 | #: models.py:73 189 | msgid "The address the email will be sent from" 190 | msgstr "Adressen e-posten vil bli sendt fra" 191 | 192 | #: models.py:74 193 | msgid "Send copies to" 194 | msgstr "Send kopi til" 195 | 196 | #: models.py:75 197 | msgid "One or more email addresses, separated by commas" 198 | msgstr "En eller flere e-postadresser, adskilt med komma" 199 | 200 | #: models.py:77 201 | msgid "Subject" 202 | msgstr "Emne" 203 | 204 | #: models.py:78 205 | msgid "Message" 206 | msgstr "Melding" 207 | 208 | #: models.py:83 209 | msgid "Form" 210 | msgstr "Skjema" 211 | 212 | #: models.py:84 213 | msgid "Forms" 214 | msgstr "Skjemaer" 215 | 216 | #: models.py:123 217 | msgid "View form on site" 218 | msgstr "Vis skjema på siden" 219 | 220 | #: models.py:124 221 | #: templates/admin/forms/change_form.html:8 222 | #: templates/admin/forms/entries.html:119 223 | msgid "View entries" 224 | msgstr "Vis innlegg" 225 | 226 | #: models.py:144 227 | msgid "Placeholder Text" 228 | msgstr "Plassholdertekst" 229 | 230 | #: models.py:151 231 | msgid "Label" 232 | msgstr "Etikett" 233 | 234 | #: models.py:152 235 | msgid "Type" 236 | msgstr "Type" 237 | 238 | #: models.py:153 239 | msgid "Required" 240 | msgstr "Påkrevd" 241 | 242 | #: models.py:154 243 | msgid "Visible" 244 | msgstr "Synlig" 245 | 246 | #: models.py:155 247 | msgid "Choices" 248 | msgstr "Valg" 249 | 250 | #: models.py:160 251 | msgid "Default value" 252 | msgstr "Standard verdi" 253 | 254 | #: models.py:163 255 | msgid "Help text" 256 | msgstr "Hjelpetekst" 257 | 258 | #: models.py:168 259 | #: templates/admin/forms/entries.html:90 260 | msgid "Field" 261 | msgstr "Felt" 262 | 263 | #: models.py:169 264 | msgid "Fields" 265 | msgstr "Felter" 266 | 267 | #: models.py:213 268 | msgid "Form entry" 269 | msgstr "Skjemautfylling" 270 | 271 | #: models.py:214 272 | msgid "Form entries" 273 | msgstr "Skjema innlegg" 274 | 275 | #: models.py:226 276 | msgid "Form field entry" 277 | msgstr "Skjemafelt oppføring" 278 | 279 | #: models.py:227 280 | msgid "Form field entries" 281 | msgstr "Skjemafeltoppføringer" 282 | 283 | #: templates/admin/forms/change_form.html:9 284 | msgid "History" 285 | msgstr "Historie" 286 | 287 | #: templates/admin/forms/change_form.html:10 288 | msgid "View on site" 289 | msgstr "Vis på siden" 290 | 291 | #: templates/admin/forms/entries.html:63 292 | msgid "No entries selected" 293 | msgstr "Ingen innlegg er valgt" 294 | 295 | #: templates/admin/forms/entries.html:66 296 | msgid "Delete selected entries?" 297 | msgstr "Slett valgte innlegg?" 298 | 299 | #: templates/admin/forms/entries.html:76 300 | msgid "Home" 301 | msgstr "Hjem" 302 | 303 | #: templates/admin/forms/entries.html:91 304 | msgid "Include" 305 | msgstr "Inkluder" 306 | 307 | #: templates/admin/forms/entries.html:92 308 | msgid "Filter by" 309 | msgstr "Filtrer etter" 310 | 311 | #: templates/admin/forms/entries.html:110 312 | msgid "All" 313 | msgstr "Alle" 314 | 315 | #: templates/admin/forms/entries.html:118 316 | #: templates/admin/forms/entries.html:151 317 | msgid "Back to form" 318 | msgstr "Tilbake til skjemaet" 319 | 320 | #: templates/admin/forms/entries.html:120 321 | msgid "Export CSV" 322 | msgstr "Exporter CSV" 323 | 324 | #: templates/admin/forms/entries.html:123 325 | msgid "Entries" 326 | msgstr "Innlegg" 327 | 328 | #: templates/admin/forms/entries.html:152 329 | msgid "Delete selected" 330 | msgstr "Slett valgte" 331 | 332 | #: templates/admin/forms/entries.html:156 333 | msgid "No entries to display" 334 | msgstr "Ingen innlegg å vise" 335 | 336 | -------------------------------------------------------------------------------- /forms_builder/forms/locale/es/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 2 | # This file is distributed under the same license as the PACKAGE package. 3 | # 4 | # , 2011. 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: \n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "POT-Creation-Date: 2011-11-27 20:19-0500\n" 10 | "PO-Revision-Date: 2011-11-28 19:07-0500\n" 11 | "Last-Translator: \n" 12 | "Language-Team: diegeus9 \n" 13 | "Language: \n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "Plural-Forms: nplurals=2; plural=(n != 1)\n" 18 | "X-Generator: Lokalize 1.2\n" 19 | 20 | #: admin.py:29 fields.py:29 21 | msgid "Email" 22 | msgstr "Email" 23 | 24 | #: admin.py:33 25 | msgid "Sites" 26 | msgstr "Sitios" 27 | 28 | #: admin.py:117 29 | #, python-format 30 | msgid "1 entry deleted" 31 | msgid_plural "%(count)s entries deleted" 32 | msgstr[0] "%(count)s entries deleted" 33 | 34 | #: admin.py:121 35 | msgid "View Entries" 36 | msgstr "Ver entradas" 37 | 38 | #: fields.py:27 39 | msgid "Single line text" 40 | msgstr "Texto de una sola linea" 41 | 42 | #: fields.py:28 43 | msgid "Multi line text" 44 | msgstr "Texto de varias lineas" 45 | 46 | #: fields.py:30 47 | msgid "Number" 48 | msgstr "Número" 49 | 50 | #: fields.py:31 51 | msgid "URL" 52 | msgstr "URL" 53 | 54 | #: fields.py:32 55 | msgid "Check box" 56 | msgstr "Casilla de verificación" 57 | 58 | #: fields.py:33 59 | msgid "Check boxes" 60 | msgstr "Casillas de verificación" 61 | 62 | #: fields.py:34 63 | msgid "Drop down" 64 | msgstr "Desplegable" 65 | 66 | #: fields.py:35 67 | msgid "Multi select" 68 | msgstr "Selección múltiple" 69 | 70 | #: fields.py:36 71 | msgid "Radio buttons" 72 | msgstr "Botón de selección única" 73 | 74 | #: fields.py:37 75 | msgid "File upload" 76 | msgstr "Carga de archivo" 77 | 78 | #: fields.py:38 79 | msgid "Date" 80 | msgstr "Fecha" 81 | 82 | #: fields.py:39 models.py:210 83 | msgid "Date/time" 84 | msgstr "Fecha/Hora" 85 | 86 | #: fields.py:40 87 | msgid "Hidden" 88 | msgstr "Oculto" 89 | 90 | #: forms.py:27 forms.py:35 forms.py:41 91 | msgid "Nothing" 92 | msgstr "Nada" 93 | 94 | #: forms.py:28 95 | msgid "Contains" 96 | msgstr "Contiene" 97 | 98 | #: forms.py:29 99 | msgid "Doesn't contain" 100 | msgstr "No contiene" 101 | 102 | #: forms.py:30 forms.py:36 103 | msgid "Equals" 104 | msgstr "Igual" 105 | 106 | #: forms.py:31 forms.py:37 107 | msgid "Doesn't equal" 108 | msgstr "No es igual" 109 | 110 | #: forms.py:42 111 | msgid "Is between" 112 | msgstr "Está entre" 113 | 114 | #: forms.py:165 115 | msgid "Checked" 116 | msgstr "Comprobado" 117 | 118 | #: forms.py:165 119 | msgid "Not checked" 120 | msgstr "No comprobado" 121 | 122 | #: forms.py:179 forms.py:194 123 | msgid "and" 124 | msgstr "y" 125 | 126 | #: models.py:54 127 | msgid "Title" 128 | msgstr "Título" 129 | 130 | #: models.py:56 131 | msgid "Intro" 132 | msgstr "Introducción" 133 | 134 | #: models.py:57 135 | msgid "Button text" 136 | msgstr "Botón de texto" 137 | 138 | #: models.py:58 139 | msgid "Submit" 140 | msgstr "Enviar" 141 | 142 | #: models.py:59 143 | msgid "Response" 144 | msgstr "Respuesta" 145 | 146 | #: models.py:60 147 | msgid "Status" 148 | msgstr "Estado" 149 | 150 | #: models.py:62 151 | msgid "Published from" 152 | msgstr "Pulbicado a partir de" 153 | 154 | #: models.py:63 155 | msgid "With published selected, won't be shown until this time" 156 | msgstr "" 157 | "Con la opción \"published\" (publicado) seleccionado, no será mostrado hasta " 158 | "esta fecha" 159 | 160 | #: models.py:65 161 | msgid "Expires on" 162 | msgstr "Expira el" 163 | 164 | #: models.py:66 165 | msgid "With published selected, won't be shown after this time" 166 | msgstr "" 167 | "Con la opción \"published\" (publicado) seleccionado, no será mostrado " 168 | "después de esta fecha" 169 | 170 | #: models.py:68 171 | msgid "Login required" 172 | msgstr "Ingreso requerido" 173 | 174 | #: models.py:69 175 | msgid "If checked, only logged in users can view the form" 176 | msgstr "" 177 | "Si es seleccionado, solo usuarios registrados pueden ver este formulario" 178 | 179 | #: models.py:70 180 | msgid "Send email" 181 | msgstr "Enviar correo electrónico" 182 | 183 | #: models.py:71 184 | msgid "If checked, the person entering the form will be sent an email" 185 | msgstr "Si es selecconado, la persona llenando el formulario enviará un correo" 186 | 187 | #: models.py:72 188 | msgid "From address" 189 | msgstr "Dirección de remitente" 190 | 191 | #: models.py:73 192 | msgid "The address the email will be sent from" 193 | msgstr "La dirección del remitente que será enviada" 194 | 195 | #: models.py:74 196 | msgid "Send copies to" 197 | msgstr "Enviar copias a" 198 | 199 | #: models.py:75 200 | msgid "One or more email addresses, separated by commas" 201 | msgstr "Una o más direcciones de correo electrónico, separadas por coma" 202 | 203 | #: models.py:77 204 | msgid "Subject" 205 | msgstr "Asunto" 206 | 207 | #: models.py:78 208 | msgid "Message" 209 | msgstr "Mensaje" 210 | 211 | #: models.py:83 212 | msgid "Form" 213 | msgstr "Formulario" 214 | 215 | #: models.py:84 216 | msgid "Forms" 217 | msgstr "Formularios" 218 | 219 | #: models.py:123 220 | msgid "View form on site" 221 | msgstr "Ver formulario en el sitio" 222 | 223 | #: models.py:124 templates/admin/forms/change_form.html:8 224 | #: templates/admin/forms/entries.html:119 225 | msgid "View entries" 226 | msgstr "Ver entradas" 227 | 228 | #: models.py:144 229 | msgid "Placeholder Text" 230 | msgstr "Texto inicial" 231 | 232 | #: models.py:151 233 | msgid "Label" 234 | msgstr "Etiqueta" 235 | 236 | #: models.py:152 237 | msgid "Type" 238 | msgstr "Tipo" 239 | 240 | #: models.py:153 241 | msgid "Required" 242 | msgstr "Requerido" 243 | 244 | #: models.py:154 245 | msgid "Visible" 246 | msgstr "Visible" 247 | 248 | #: models.py:155 249 | msgid "Choices" 250 | msgstr "Opciones" 251 | 252 | #: models.py:160 253 | msgid "Default value" 254 | msgstr "Valor por omisión" 255 | 256 | #: models.py:163 257 | msgid "Help text" 258 | msgstr "Texto de ayuda" 259 | 260 | #: models.py:168 templates/admin/forms/entries.html:90 261 | msgid "Field" 262 | msgstr "Campo" 263 | 264 | #: models.py:169 265 | msgid "Fields" 266 | msgstr "Campos" 267 | 268 | #: models.py:213 269 | msgid "Form entry" 270 | msgstr "Entrada de Formulario" 271 | 272 | #: models.py:214 273 | msgid "Form entries" 274 | msgstr "Entradas de Formulario" 275 | 276 | #: models.py:226 277 | msgid "Form field entry" 278 | msgstr "Entrada de campo de formulario" 279 | 280 | #: models.py:227 281 | msgid "Form field entries" 282 | msgstr "Entradas de campo de formulario" 283 | 284 | #: templates/admin/forms/change_form.html:9 285 | msgid "History" 286 | msgstr "Historia" 287 | 288 | #: templates/admin/forms/change_form.html:10 289 | msgid "View on site" 290 | msgstr "Ver en el sitio" 291 | 292 | #: templates/admin/forms/entries.html:63 293 | msgid "No entries selected" 294 | msgstr "Ninguna entrada seleccionada" 295 | 296 | #: templates/admin/forms/entries.html:66 297 | msgid "Delete selected entries?" 298 | msgstr "Eliminar las entradas seleccionadas?" 299 | 300 | #: templates/admin/forms/entries.html:76 301 | msgid "Home" 302 | msgstr "Inicio" 303 | 304 | #: templates/admin/forms/entries.html:91 305 | msgid "Include" 306 | msgstr "Incluir" 307 | 308 | #: templates/admin/forms/entries.html:92 309 | msgid "Filter by" 310 | msgstr "Filtrar" 311 | 312 | #: templates/admin/forms/entries.html:110 313 | msgid "All" 314 | msgstr "Todos" 315 | 316 | #: templates/admin/forms/entries.html:118 317 | #: templates/admin/forms/entries.html:156 318 | msgid "Back to form" 319 | msgstr "Volver al formulario" 320 | 321 | #: templates/admin/forms/entries.html:120 322 | msgid "Export CSV" 323 | msgstr "Exportar como CSV" 324 | 325 | #: templates/admin/forms/entries.html:123 326 | msgid "Entries" 327 | msgstr "Entradas" 328 | 329 | #: templates/admin/forms/entries.html:157 330 | msgid "Delete selected" 331 | msgstr "Eliminar seleccionados" 332 | 333 | #: templates/admin/forms/entries.html:161 334 | msgid "No entries to display" 335 | msgstr "Ninguna entrada que mostrar" 336 | 337 | 338 | -------------------------------------------------------------------------------- /forms_builder/forms/locale/zh/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: forms_builder\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2016-01-13 17:23+0800\n" 11 | "PO-Revision-Date: 2016-01-13 17:54+0800\n" 12 | "Language: zh_CN\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: 8bit\n" 16 | "Plural-Forms: nplurals=1; plural=0;\n" 17 | "Last-Translator: Bill Zhang\n" 18 | "Language-Team: 张春晖 \n" 19 | "X-Generator: Poedit 1.8.6\n" 20 | "X-Poedit-SourceCharset: UTF-8\n" 21 | 22 | #: admin.py:40 fields.py:33 23 | msgid "Email" 24 | msgstr "电子邮件" 25 | 26 | #: admin.py:45 models.py:59 models.py:166 27 | msgid "Slug" 28 | msgstr "slug节点" 29 | 30 | #: admin.py:48 31 | msgid "Sites" 32 | msgstr "站点" 33 | 34 | #: admin.py:177 35 | #, python-format 36 | msgid "1 entry deleted" 37 | msgid_plural "%(count)s entries deleted" 38 | msgstr[0] "%(count)s 条目已删除。" 39 | 40 | #: admin.py:181 41 | msgid "View Entries" 42 | msgstr "查看条目" 43 | 44 | #: fields.py:31 45 | msgid "Single line text" 46 | msgstr "单行文本" 47 | 48 | #: fields.py:32 49 | msgid "Multi line text" 50 | msgstr "多行文本" 51 | 52 | #: fields.py:34 53 | msgid "Number" 54 | msgstr "数字" 55 | 56 | #: fields.py:35 57 | msgid "URL" 58 | msgstr "网址" 59 | 60 | #: fields.py:36 61 | msgid "Check box" 62 | msgstr "复选框" 63 | 64 | #: fields.py:37 65 | msgid "Check boxes" 66 | msgstr "复选框" 67 | 68 | #: fields.py:38 69 | msgid "Drop down" 70 | msgstr "下拉" 71 | 72 | #: fields.py:39 73 | msgid "Multi select" 74 | msgstr "多选择" 75 | 76 | #: fields.py:40 77 | msgid "Radio buttons" 78 | msgstr "单选按钮" 79 | 80 | #: fields.py:41 81 | msgid "File upload" 82 | msgstr "上传文件" 83 | 84 | #: fields.py:42 85 | msgid "Date" 86 | msgstr "日期" 87 | 88 | #: fields.py:43 models.py:234 89 | msgid "Date/time" 90 | msgstr "日期时间" 91 | 92 | #: fields.py:44 93 | msgid "Date of birth" 94 | msgstr "出生日期" 95 | 96 | #: fields.py:45 97 | msgid "Hidden" 98 | msgstr "隐藏" 99 | 100 | #: forms.py:52 forms.py:61 forms.py:68 forms.py:77 101 | msgid "Nothing" 102 | msgstr "无" 103 | 104 | #: forms.py:53 105 | msgid "Contains" 106 | msgstr "包含" 107 | 108 | #: forms.py:54 109 | msgid "Doesn't contain" 110 | msgstr "不含" 111 | 112 | #: forms.py:55 113 | msgid "Equals" 114 | msgstr "等于" 115 | 116 | #: forms.py:56 117 | msgid "Doesn't equal" 118 | msgstr "不等于" 119 | 120 | #: forms.py:62 121 | msgid "Equals any" 122 | msgstr "等于任何" 123 | 124 | #: forms.py:63 125 | msgid "Doesn't equal any" 126 | msgstr "不等于任何" 127 | 128 | #: forms.py:69 129 | msgid "Contains any" 130 | msgstr "包含任何" 131 | 132 | #: forms.py:70 133 | msgid "Contains all" 134 | msgstr "包含所有" 135 | 136 | #: forms.py:71 137 | msgid "Doesn't contain any" 138 | msgstr "不含任何" 139 | 140 | #: forms.py:72 141 | msgid "Doesn't contain all" 142 | msgstr "不包含所有" 143 | 144 | #: forms.py:78 145 | msgid "Is between" 146 | msgstr "在之间" 147 | 148 | #: forms.py:276 149 | msgid "Checked" 150 | msgstr "选中" 151 | 152 | #: forms.py:276 153 | msgid "Not checked" 154 | msgstr "未选中" 155 | 156 | #: forms.py:299 forms.py:314 157 | msgid "and" 158 | msgstr "和" 159 | 160 | #: models.py:20 161 | msgid "Draft" 162 | msgstr "草稿" 163 | 164 | #: models.py:21 165 | msgid "Published" 166 | msgstr "已发布" 167 | 168 | #: models.py:58 169 | msgid "Title" 170 | msgstr "标题" 171 | 172 | #: models.py:61 173 | msgid "Intro" 174 | msgstr "简介" 175 | 176 | #: models.py:62 177 | msgid "Button text" 178 | msgstr "按钮文字" 179 | 180 | #: models.py:63 181 | msgid "Submit" 182 | msgstr "提交" 183 | 184 | #: models.py:64 185 | msgid "Response" 186 | msgstr "响应内容" 187 | 188 | #: models.py:65 189 | msgid "Redirect url" 190 | msgstr "重定向URL" 191 | 192 | #: models.py:67 193 | msgid "An alternate URL to redirect to after form submission" 194 | msgstr "表单提交后备用重定向URL" 195 | 196 | #: models.py:68 197 | msgid "Status" 198 | msgstr "状态" 199 | 200 | #: models.py:70 201 | msgid "Published from" 202 | msgstr "发布时间从" 203 | 204 | #: models.py:71 205 | msgid "With published selected, won't be shown until this time" 206 | msgstr "若选定发布,直到此时才会显示" 207 | 208 | #: models.py:73 209 | msgid "Expires on" 210 | msgstr "到期" 211 | 212 | #: models.py:74 213 | msgid "With published selected, won't be shown after this time" 214 | msgstr "若选定发布,此时之后将会隐藏" 215 | 216 | #: models.py:76 217 | msgid "Login required" 218 | msgstr "需要登录" 219 | 220 | #: models.py:77 221 | msgid "If checked, only logged in users can view the form" 222 | msgstr "如果选中,只有登录用户可以查看表单" 223 | 224 | #: models.py:78 225 | msgid "Send email" 226 | msgstr "发电子邮件" 227 | 228 | #: models.py:79 229 | msgid "If checked, the person entering the form will be sent an email" 230 | msgstr "如果选中,将会发送一封电子邮件给进入该表单的用户" 231 | 232 | #: models.py:80 233 | msgid "From address" 234 | msgstr "发件人地址" 235 | 236 | #: models.py:81 237 | msgid "The address the email will be sent from" 238 | msgstr "发件人地址" 239 | 240 | #: models.py:82 241 | msgid "Send copies to" 242 | msgstr "抄送" 243 | 244 | #: models.py:83 245 | msgid "One or more email addresses, separated by commas" 246 | msgstr "一个或多个电子邮件地址,以逗号分隔" 247 | 248 | #: models.py:85 249 | msgid "Subject" 250 | msgstr "邮件主题" 251 | 252 | #: models.py:86 253 | msgid "Message" 254 | msgstr "邮件内容" 255 | 256 | #: models.py:91 257 | msgid "Form" 258 | msgstr "表单" 259 | 260 | #: models.py:92 261 | msgid "Forms" 262 | msgstr "表单" 263 | 264 | #: models.py:139 265 | msgid "View form on site" 266 | msgstr "在站点查看表单" 267 | 268 | #: models.py:140 269 | msgid "Filter entries" 270 | msgstr "过滤器的项" 271 | 272 | #: models.py:141 273 | msgid "View all entries" 274 | msgstr "查看所有条目" 275 | 276 | #: models.py:142 277 | msgid "Export all entries" 278 | msgstr "导出所有条目" 279 | 280 | #: models.py:165 281 | msgid "Label" 282 | msgstr "标签" 283 | 284 | #: models.py:168 285 | msgid "Type" 286 | msgstr "类型" 287 | 288 | #: models.py:169 289 | msgid "Required" 290 | msgstr "必填" 291 | 292 | #: models.py:170 293 | msgid "Visible" 294 | msgstr "可见" 295 | 296 | #: models.py:171 297 | msgid "Choices" 298 | msgstr "选项" 299 | 300 | #: models.py:176 301 | msgid "Default value" 302 | msgstr "默认值" 303 | 304 | #: models.py:178 305 | msgid "Placeholder Text" 306 | msgstr "占位文本" 307 | 308 | #: models.py:180 309 | msgid "Help text" 310 | msgstr "帮助文本" 311 | 312 | #: models.py:185 templates/admin/forms/entries.html:88 313 | msgid "Field" 314 | msgstr "字段" 315 | 316 | #: models.py:186 317 | msgid "Fields" 318 | msgstr "字段" 319 | 320 | #: models.py:237 321 | msgid "Form entry" 322 | msgstr "表单条目" 323 | 324 | #: models.py:238 325 | msgid "Form entries" 326 | msgstr "表单条目" 327 | 328 | #: models.py:252 329 | msgid "Form field entry" 330 | msgstr "表单字段项" 331 | 332 | #: models.py:253 333 | msgid "Form field entries" 334 | msgstr "表单字段条目" 335 | 336 | #: models.py:281 337 | msgid "Order" 338 | msgstr "顺序" 339 | 340 | #: templates/admin/forms/change_form.html:9 341 | #: templates/admin/forms/entries.html:117 342 | msgid "View entries" 343 | msgstr "查看条目" 344 | 345 | #: templates/admin/forms/change_form.html:12 346 | msgid "History" 347 | msgstr "历史" 348 | 349 | #: templates/admin/forms/change_form.html:16 350 | msgid "View on site" 351 | msgstr "在站点查看" 352 | 353 | #: templates/admin/forms/entries.html:62 354 | msgid "No entries selected" 355 | msgstr "没有选择条目" 356 | 357 | #: templates/admin/forms/entries.html:65 358 | msgid "Delete selected entries?" 359 | msgstr "删除所选条目?" 360 | 361 | #: templates/admin/forms/entries.html:74 362 | msgid "Home" 363 | msgstr "主页" 364 | 365 | #: templates/admin/forms/entries.html:89 366 | msgid "Include" 367 | msgstr "包括" 368 | 369 | #: templates/admin/forms/entries.html:90 370 | msgid "Filter by" 371 | msgstr "过滤" 372 | 373 | #: templates/admin/forms/entries.html:108 374 | msgid "All" 375 | msgstr "所有" 376 | 377 | #: templates/admin/forms/entries.html:116 378 | #: templates/admin/forms/entries.html:157 379 | msgid "Back to form" 380 | msgstr "返回表单" 381 | 382 | #: templates/admin/forms/entries.html:118 383 | msgid "Export CSV" 384 | msgstr "导出CSV" 385 | 386 | #: templates/admin/forms/entries.html:120 387 | msgid "Export XLS" 388 | msgstr "出口XLS" 389 | 390 | #: templates/admin/forms/entries.html:124 391 | msgid "Entries" 392 | msgstr "项" 393 | 394 | #: templates/admin/forms/entries.html:158 395 | msgid "Delete selected" 396 | msgstr "删除所选" 397 | 398 | #: templates/admin/forms/entries.html:162 399 | msgid "No entries to display" 400 | msgstr "没有项目可显示" 401 | -------------------------------------------------------------------------------- /forms_builder/forms/locale/fr/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: django-forms-builder\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2012-06-27 12:44+0200\n" 11 | "PO-Revision-Date: 2012-06-27 12:45+0100\n" 12 | "Last-Translator: Dominique Guardiola \n" 13 | "Language-Team: Quindoe \n" 14 | "Language: \n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=2; plural=(n != 1)\n" 19 | "X-Poedit-Language: French\n" 20 | "X-Poedit-Country: FRANCE\n" 21 | 22 | #: admin.py:29 23 | #: fields.py:29 24 | msgid "Email" 25 | msgstr "Courriel" 26 | 27 | #: admin.py:33 28 | msgid "Sites" 29 | msgstr "Sites" 30 | 31 | #: admin.py:125 32 | #, python-format 33 | msgid "1 entry deleted" 34 | msgid_plural "%(count)s entries deleted" 35 | msgstr[0] "Une réponse effacée" 36 | msgstr[1] "%(count)s réponses effacées" 37 | 38 | #: admin.py:129 39 | msgid "View Entries" 40 | msgstr "Voir les réponses" 41 | 42 | #: fields.py:27 43 | msgid "Single line text" 44 | msgstr "Texte sur une ligne" 45 | 46 | #: fields.py:28 47 | msgid "Multi line text" 48 | msgstr "Texte sur plusieurs lignes" 49 | 50 | #: fields.py:30 51 | msgid "Number" 52 | msgstr "Numéro" 53 | 54 | #: fields.py:31 55 | msgid "URL" 56 | msgstr "URL" 57 | 58 | #: fields.py:32 59 | msgid "Check box" 60 | msgstr "Case à cocher" 61 | 62 | #: fields.py:33 63 | msgid "Check boxes" 64 | msgstr "Cases à cocher" 65 | 66 | #: fields.py:34 67 | msgid "Drop down" 68 | msgstr "Menu déroulant" 69 | 70 | #: fields.py:35 71 | msgid "Multi select" 72 | msgstr "Sélection multiple" 73 | 74 | #: fields.py:36 75 | msgid "Radio buttons" 76 | msgstr "Choix exclusifs" 77 | 78 | #: fields.py:37 79 | msgid "File upload" 80 | msgstr "Envoi de fichier" 81 | 82 | #: fields.py:38 83 | msgid "Date" 84 | msgstr "Date" 85 | 86 | #: fields.py:39 87 | #: models.py:218 88 | msgid "Date/time" 89 | msgstr "Date et heure" 90 | 91 | #: fields.py:40 92 | msgid "Hidden" 93 | msgstr "Caché" 94 | 95 | #: forms.py:30 96 | #: forms.py:38 97 | #: forms.py:44 98 | msgid "Nothing" 99 | msgstr "Rien" 100 | 101 | #: forms.py:31 102 | msgid "Contains" 103 | msgstr "Contient" 104 | 105 | #: forms.py:32 106 | msgid "Doesn't contain" 107 | msgstr "Ne contient pas" 108 | 109 | #: forms.py:33 110 | #: forms.py:39 111 | msgid "Equals" 112 | msgstr "Est égal à" 113 | 114 | #: forms.py:34 115 | #: forms.py:40 116 | msgid "Doesn't equal" 117 | msgstr "N'est pas égal à" 118 | 119 | #: forms.py:45 120 | msgid "Is between" 121 | msgstr "se situe entre" 122 | 123 | #: forms.py:212 124 | msgid "Checked" 125 | msgstr "coché" 126 | 127 | #: forms.py:212 128 | msgid "Not checked" 129 | msgstr "non coché" 130 | 131 | #: forms.py:226 132 | #: forms.py:241 133 | msgid "and" 134 | msgstr "et" 135 | 136 | #: models.py:18 137 | msgid "Draft" 138 | msgstr "Brouillon" 139 | 140 | #: models.py:19 141 | msgid "Published" 142 | msgstr "Publié" 143 | 144 | #: models.py:55 145 | msgid "Title" 146 | msgstr "Titre" 147 | 148 | #: models.py:57 149 | msgid "Intro" 150 | msgstr "Intro" 151 | 152 | #: models.py:58 153 | msgid "Button text" 154 | msgstr "Texte du bouton" 155 | 156 | #: models.py:59 157 | msgid "Submit" 158 | msgstr "Envoyer" 159 | 160 | #: models.py:60 161 | #, fuzzy 162 | msgid "Response" 163 | msgstr "Message à la réception" 164 | 165 | #: models.py:61 166 | msgid "Status" 167 | msgstr "Statut" 168 | 169 | #: models.py:63 170 | msgid "Published from" 171 | msgstr "Publié depuis" 172 | 173 | #: models.py:64 174 | msgid "With published selected, won't be shown until this time" 175 | msgstr "Avec \"publié\" sélectionné, ne sera pas publié avant cette date" 176 | 177 | #: models.py:66 178 | msgid "Expires on" 179 | msgstr "Expire le" 180 | 181 | #: models.py:67 182 | msgid "With published selected, won't be shown after this time" 183 | msgstr "Avec \"publié\" sélectionné, ne sera pas publié avant cette date" 184 | 185 | #: models.py:69 186 | msgid "Login required" 187 | msgstr "Identification requise" 188 | 189 | #: models.py:70 190 | msgid "If checked, only logged in users can view the form" 191 | msgstr "Si coché, seules les visiteurs identifiés pourront voir le formulaire" 192 | 193 | #: models.py:71 194 | msgid "Send email" 195 | msgstr "Envoyer un courriel" 196 | 197 | #: models.py:72 198 | msgid "If checked, the person entering the form will be sent an email" 199 | msgstr "Si coché, les personnes répondant au formulaire recevront un courriel" 200 | 201 | #: models.py:73 202 | msgid "From address" 203 | msgstr "Adresse d'expéditeur" 204 | 205 | #: models.py:74 206 | msgid "The address the email will be sent from" 207 | msgstr "L'adresse à partir de laquelle sera envoyé le courriel" 208 | 209 | #: models.py:75 210 | msgid "Send copies to" 211 | msgstr "Envoyer des copies à" 212 | 213 | #: models.py:76 214 | msgid "One or more email addresses, separated by commas" 215 | msgstr "Une ou plusieurs adresses email séparées par des virgules" 216 | 217 | #: models.py:78 218 | msgid "Subject" 219 | msgstr "Sujet" 220 | 221 | #: models.py:79 222 | msgid "Message" 223 | msgstr "Message" 224 | 225 | #: models.py:84 226 | msgid "Form" 227 | msgstr "Formulaire" 228 | 229 | #: models.py:85 230 | msgid "Forms" 231 | msgstr "Formulaires" 232 | 233 | #: models.py:124 234 | msgid "View form on site" 235 | msgstr "Voir sur le site" 236 | 237 | #: models.py:125 238 | msgid "Filter entries" 239 | msgstr "Filtrer les réponses" 240 | 241 | #: models.py:126 242 | msgid "View all entries" 243 | msgstr "Voir toutes les réponses" 244 | 245 | #: models.py:127 246 | msgid "Export all entries" 247 | msgstr "Exporter toutes les réponses" 248 | 249 | #: models.py:149 250 | msgid "Label" 251 | msgstr "Libellé" 252 | 253 | #: models.py:150 254 | msgid "Slug" 255 | msgstr "Slug" 256 | 257 | #: models.py:152 258 | msgid "Type" 259 | msgstr "Type" 260 | 261 | #: models.py:153 262 | msgid "Required" 263 | msgstr "Obligatoire" 264 | 265 | #: models.py:154 266 | msgid "Visible" 267 | msgstr "Visible" 268 | 269 | #: models.py:155 270 | msgid "Choices" 271 | msgstr "Choix" 272 | 273 | #: models.py:160 274 | msgid "Default value" 275 | msgstr "Valeur par défaut" 276 | 277 | #: models.py:162 278 | msgid "Placeholder Text" 279 | msgstr "Texte par défaut" 280 | 281 | #: models.py:164 282 | msgid "Help text" 283 | msgstr "Texte d'aide" 284 | 285 | #: models.py:169 286 | #: templates/admin/forms/entries.html:87 287 | msgid "Field" 288 | msgstr "Champ" 289 | 290 | #: models.py:170 291 | msgid "Fields" 292 | msgstr "Champs" 293 | 294 | #: models.py:221 295 | msgid "Form entry" 296 | msgstr "Réponse au formulaire" 297 | 298 | #: models.py:222 299 | msgid "Form entries" 300 | msgstr "Réponses au formulaire" 301 | 302 | #: models.py:236 303 | msgid "Form field entry" 304 | msgstr "Réponse à un champ de formulaire" 305 | 306 | #: models.py:237 307 | msgid "Form field entries" 308 | msgstr "Réponses à un champ de formulaire" 309 | 310 | #: models.py:265 311 | msgid "Order" 312 | msgstr "Ordre" 313 | 314 | #: templates/admin/forms/change_form.html:10 315 | #: templates/admin/forms/entries.html:116 316 | msgid "View entries" 317 | msgstr "Voir les réponses" 318 | 319 | #: templates/admin/forms/change_form.html:13 320 | msgid "History" 321 | msgstr "Historique" 322 | 323 | #: templates/admin/forms/change_form.html:17 324 | msgid "View on site" 325 | msgstr "Voir sur le site" 326 | 327 | #: templates/admin/forms/entries.html:61 328 | msgid "No entries selected" 329 | msgstr "Pas de réponses sélectionnées" 330 | 331 | #: templates/admin/forms/entries.html:64 332 | msgid "Delete selected entries?" 333 | msgstr "Effacer les réponses sélectionnées?" 334 | 335 | #: templates/admin/forms/entries.html:73 336 | msgid "Home" 337 | msgstr "Accueil" 338 | 339 | #: templates/admin/forms/entries.html:88 340 | msgid "Include" 341 | msgstr "Inclure" 342 | 343 | #: templates/admin/forms/entries.html:89 344 | msgid "Filter by" 345 | msgstr "Filtrer par" 346 | 347 | #: templates/admin/forms/entries.html:107 348 | msgid "All" 349 | msgstr "Tous" 350 | 351 | #: templates/admin/forms/entries.html:115 352 | #: templates/admin/forms/entries.html:155 353 | msgid "Back to form" 354 | msgstr "Retour au formulaire" 355 | 356 | #: templates/admin/forms/entries.html:117 357 | msgid "Export CSV" 358 | msgstr "Exporter en tableur CSV" 359 | 360 | #: templates/admin/forms/entries.html:120 361 | msgid "Entries" 362 | msgstr "Réponses" 363 | 364 | #: templates/admin/forms/entries.html:156 365 | msgid "Delete selected" 366 | msgstr "Effacer la sélection" 367 | 368 | #: templates/admin/forms/entries.html:160 369 | msgid "No entries to display" 370 | msgstr "Pas de réponses à afficher" 371 | 372 | -------------------------------------------------------------------------------- /forms_builder/forms/locale/ru/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Russian Translation for stephenmcd's django-forms-builder 2 | # Copyright (C) 2012 THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # Fill Q , 2012. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2012-09-06 19:08+0400\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: Fill Q \n" 14 | "Language-Team: RU LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" 20 | 21 | #: admin.py:36 fields.py:30 22 | msgid "Email" 23 | msgstr "Емайл" 24 | 25 | #: admin.py:41 models.py:55 models.py:142 26 | msgid "Slug" 27 | msgstr "Краткое имя(slug)" 28 | 29 | #: admin.py:44 30 | msgid "Sites" 31 | msgstr "Сайты" 32 | 33 | #: admin.py:158 34 | #, python-format 35 | msgid "1 entry deleted" 36 | msgid_plural "%(count)s entries deleted" 37 | msgstr[0] "1 запись удалена" 38 | msgstr[1] "%(count)s записи удалено" 39 | msgstr[2] "%(count)s записей удалено" 40 | 41 | #: admin.py:162 42 | #, fuzzy 43 | msgid "View Entries" 44 | msgstr "Показать данные" 45 | 46 | #: fields.py:28 47 | msgid "Single line text" 48 | msgstr "Однострочный текст" 49 | 50 | #: fields.py:29 51 | msgid "Multi line text" 52 | msgstr "Многострочный текст" 53 | 54 | #: fields.py:31 55 | msgid "Number" 56 | msgstr "Номер" 57 | 58 | #: fields.py:32 59 | msgid "URL" 60 | msgstr "URL" 61 | 62 | #: fields.py:33 63 | msgid "Check box" 64 | msgstr "Чекбокс" 65 | 66 | #: fields.py:34 67 | msgid "Check boxes" 68 | msgstr "Чекбоксы" 69 | 70 | #: fields.py:35 71 | msgid "Drop down" 72 | msgstr "Выпадающее меню" 73 | 74 | #: fields.py:36 75 | msgid "Multi select" 76 | msgstr "Мультивыбор" 77 | 78 | #: fields.py:37 79 | msgid "Radio buttons" 80 | msgstr "Радио-кнопки" 81 | 82 | #: fields.py:38 83 | msgid "File upload" 84 | msgstr "Загрузка файлов" 85 | 86 | #: fields.py:39 87 | msgid "Date" 88 | msgstr "Дата" 89 | 90 | #: fields.py:40 models.py:210 91 | msgid "Date/time" 92 | msgstr "Дата/время" 93 | 94 | #: fields.py:41 95 | msgid "Date of birth" 96 | msgstr "Дата рождения" 97 | 98 | #: fields.py:42 99 | msgid "Hidden" 100 | msgstr "Скрыто" 101 | 102 | #: forms.py:30 forms.py:38 forms.py:44 103 | msgid "Nothing" 104 | msgstr "Ничего" 105 | 106 | #: forms.py:31 107 | msgid "Contains" 108 | msgstr "Содержит" 109 | 110 | #: forms.py:32 111 | msgid "Doesn't contain" 112 | msgstr "Не содержит" 113 | 114 | #: forms.py:33 forms.py:39 115 | msgid "Equals" 116 | msgstr "Равняется" 117 | 118 | #: forms.py:34 forms.py:40 119 | msgid "Doesn't equal" 120 | msgstr "Не равняется" 121 | 122 | #: forms.py:45 123 | msgid "Is between" 124 | msgstr "Между" 125 | 126 | #: forms.py:218 127 | msgid "Checked" 128 | msgstr "Выбрано" 129 | 130 | #: forms.py:218 131 | msgid "Not checked" 132 | msgstr "Не выбрано" 133 | 134 | #: forms.py:232 forms.py:247 135 | msgid "and" 136 | msgstr "и" 137 | 138 | #: models.py:17 139 | msgid "Draft" 140 | msgstr "Черновик" 141 | 142 | #: models.py:18 143 | msgid "Published" 144 | msgstr "Опубликовано" 145 | 146 | #: models.py:54 147 | msgid "Title" 148 | msgstr "Название" 149 | 150 | #: models.py:57 151 | msgid "Intro" 152 | msgstr "Вступление" 153 | 154 | #: models.py:58 155 | msgid "Button text" 156 | msgstr "Текст кнопки" 157 | 158 | #: models.py:59 159 | msgid "Submit" 160 | msgstr "Отправить" 161 | 162 | #: models.py:60 163 | msgid "Response" 164 | msgstr "Ответ" 165 | 166 | #: models.py:61 167 | msgid "Status" 168 | msgstr "Статус" 169 | 170 | #: models.py:63 171 | msgid "Published from" 172 | msgstr "Опубликовано с" 173 | 174 | #: models.py:64 175 | msgid "With published selected, won't be shown until this time" 176 | msgstr "" 177 | "Форма не будет показана до даты/времени которое указано в публикации(если " 178 | "отмечено \"опубликовать\")." 179 | 180 | #: models.py:66 181 | msgid "Expires on" 182 | msgstr "Действительно до" 183 | 184 | #: models.py:67 185 | msgid "With published selected, won't be shown after this time" 186 | msgstr "" 187 | "Форма не будет показана после даты/времени которое указано в публикации(если " 188 | "отмечено \"опубликовать\")." 189 | 190 | #: models.py:69 191 | msgid "Login required" 192 | msgstr "Требуется авторизация" 193 | 194 | #: models.py:70 195 | msgid "If checked, only logged in users can view the form" 196 | msgstr "" 197 | "Если отмечено, только зарегестрированные пользователи могут видеть форму" 198 | 199 | #: models.py:71 200 | msgid "Send email" 201 | msgstr "Отослать емайл" 202 | 203 | #: models.py:72 204 | msgid "If checked, the person entering the form will be sent an email" 205 | msgstr "Если выбрано, данные формы будут отосланы на емайл" 206 | 207 | #: models.py:73 208 | msgid "From address" 209 | msgstr "С адреса" 210 | 211 | #: models.py:74 212 | msgid "The address the email will be sent from" 213 | msgstr "Адрес емайла с которого будет отослано письмо" 214 | 215 | #: models.py:75 216 | msgid "Send copies to" 217 | msgstr "Отправить копию по адресу" 218 | 219 | #: models.py:76 220 | msgid "One or more email addresses, separated by commas" 221 | msgstr "Одно или более емайлов, разделенных запятой" 222 | 223 | #: models.py:78 224 | msgid "Subject" 225 | msgstr "Тема" 226 | 227 | #: models.py:79 228 | msgid "Message" 229 | msgstr "Сообщение" 230 | 231 | #: models.py:84 232 | msgid "Form" 233 | msgstr "Форму" 234 | 235 | #: models.py:85 236 | msgid "Forms" 237 | msgstr "Формы" 238 | 239 | #: models.py:116 240 | msgid "View form on site" 241 | msgstr "Показать форму на сайте" 242 | 243 | #: models.py:117 244 | msgid "Filter entries" 245 | msgstr "Данные формы" 246 | 247 | #: models.py:118 248 | msgid "View all entries" 249 | msgstr "Показать все данные" 250 | 251 | #: models.py:119 252 | msgid "Export all entries" 253 | msgstr "Экспорт всех данных" 254 | 255 | #: models.py:141 256 | msgid "Label" 257 | msgstr "Метка" 258 | 259 | #: models.py:144 260 | msgid "Type" 261 | msgstr "Тип" 262 | 263 | #: models.py:145 264 | msgid "Required" 265 | msgstr "Обязательное" 266 | 267 | #: models.py:146 268 | msgid "Visible" 269 | msgstr "Видимое" 270 | 271 | #: models.py:147 272 | msgid "Choices" 273 | msgstr "Выбор" 274 | 275 | #: models.py:152 276 | msgid "Default value" 277 | msgstr "Стандартное значение" 278 | 279 | #: models.py:154 280 | msgid "Placeholder Text" 281 | msgstr "Текст заполнителя" 282 | 283 | #: models.py:156 284 | msgid "Help text" 285 | msgstr "Текст помощи" 286 | 287 | #: models.py:161 templates/admin/forms/entries.html:87 288 | msgid "Field" 289 | msgstr "Поле" 290 | 291 | #: models.py:162 292 | msgid "Fields" 293 | msgstr "Поля" 294 | 295 | #: models.py:213 296 | msgid "Form entry" 297 | msgstr "Данные формы" 298 | 299 | #: models.py:214 300 | msgid "Form entries" 301 | msgstr "Данные формы" 302 | 303 | #: models.py:228 304 | msgid "Form field entry" 305 | msgstr "Данные поля формы" 306 | 307 | #: models.py:229 308 | msgid "Form field entries" 309 | msgstr "Данные поля формы" 310 | 311 | #: models.py:257 312 | msgid "Order" 313 | msgstr "Сортировка" 314 | 315 | #: templates/admin/forms/change_form.html:10 316 | #: templates/admin/forms/entries.html:116 317 | msgid "View entries" 318 | msgstr "Показать данные формы" 319 | 320 | #: templates/admin/forms/change_form.html:13 321 | msgid "History" 322 | msgstr "История" 323 | 324 | #: templates/admin/forms/change_form.html:17 325 | msgid "View on site" 326 | msgstr "Показать на сайте" 327 | 328 | #: templates/admin/forms/entries.html:61 329 | msgid "No entries selected" 330 | msgstr "Нет выбранных записей" 331 | 332 | #: templates/admin/forms/entries.html:64 333 | msgid "Delete selected entries?" 334 | msgstr "Удалить выбранные записи?" 335 | 336 | #: templates/admin/forms/entries.html:73 337 | msgid "Home" 338 | msgstr "Домой" 339 | 340 | #: templates/admin/forms/entries.html:88 341 | msgid "Include" 342 | msgstr "Содержит" 343 | 344 | #: templates/admin/forms/entries.html:89 345 | msgid "Filter by" 346 | msgstr "Фильтровать по" 347 | 348 | #: templates/admin/forms/entries.html:107 349 | msgid "All" 350 | msgstr "Все" 351 | 352 | #: templates/admin/forms/entries.html:115 353 | #: templates/admin/forms/entries.html:158 354 | msgid "Back to form" 355 | msgstr "Вернуться к форме" 356 | 357 | #: templates/admin/forms/entries.html:117 358 | msgid "Export CSV" 359 | msgstr "Экспортировать в CSV" 360 | 361 | #: templates/admin/forms/entries.html:119 362 | msgid "Export XLS" 363 | msgstr "Экспортировать в XLS" 364 | 365 | #: templates/admin/forms/entries.html:123 366 | #, fuzzy 367 | msgid "Entries" 368 | msgstr "Данные" 369 | 370 | #: templates/admin/forms/entries.html:159 371 | #, fuzzy 372 | msgid "Delete selected" 373 | msgstr "Удалить выбранные" 374 | 375 | #: templates/admin/forms/entries.html:163 376 | msgid "No entries to display" 377 | msgstr "Нет данных для показа" 378 | -------------------------------------------------------------------------------- /forms_builder/forms/locale/pt_BR/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Brazilian portuguese translation for stephenmcd's django-forms-builder 2 | # This file is distributed under the same license as the PACKAGE package. 3 | # Tiago Myhro Ilieve , 2013. 4 | # 5 | #, fuzzy 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: django-forms-builder\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2013-04-23 23:35-0300\n" 11 | "PO-Revision-Date: 2013-05-03 00:10-0300\n" 12 | "Last-Translator: Tiago Myhro Ilieve \n" 13 | "Language-Team: \n" 14 | "Language: pt-BR\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=2; plural=(n != 1)\n" 19 | 20 | #: admin.py:37 fields.py:32 21 | msgid "Email" 22 | msgstr "E-mail" 23 | 24 | #: admin.py:42 models.py:55 models.py:142 25 | msgid "Slug" 26 | msgstr "Slug" 27 | 28 | #: admin.py:45 29 | msgid "Sites" 30 | msgstr "Sites" 31 | 32 | #: admin.py:168 33 | #, python-format 34 | msgid "1 entry deleted" 35 | msgid_plural "%(count)s entries deleted" 36 | msgstr[0] "1 registro apagado" 37 | msgstr[1] "%(count)s registros apagados" 38 | 39 | #: admin.py:172 40 | msgid "View Entries" 41 | msgstr "Ver Registros" 42 | 43 | #: fields.py:30 44 | msgid "Single line text" 45 | msgstr "Texto de uma linha" 46 | 47 | #: fields.py:31 48 | msgid "Multi line text" 49 | msgstr "Texto de múltiplas linhas" 50 | 51 | #: fields.py:33 52 | msgid "Number" 53 | msgstr "Número" 54 | 55 | #: fields.py:34 56 | msgid "URL" 57 | msgstr "URL" 58 | 59 | #: fields.py:35 60 | msgid "Check box" 61 | msgstr "Caixa de seleção" 62 | 63 | #: fields.py:36 64 | msgid "Check boxes" 65 | msgstr "Caixas de seleção" 66 | 67 | #: fields.py:37 68 | msgid "Drop down" 69 | msgstr "Menu drop down" 70 | 71 | #: fields.py:38 72 | msgid "Multi select" 73 | msgstr "Caixa de listagem" 74 | 75 | #: fields.py:39 76 | msgid "Radio buttons" 77 | msgstr "Botões de opção" 78 | 79 | #: fields.py:40 80 | msgid "File upload" 81 | msgstr "Envio de arquivo" 82 | 83 | #: fields.py:41 84 | msgid "Date" 85 | msgstr "Data" 86 | 87 | #: fields.py:42 models.py:210 88 | msgid "Date/time" 89 | msgstr "Data/hora" 90 | 91 | #: fields.py:43 92 | msgid "Date of birth" 93 | msgstr "Data de nascimento" 94 | 95 | #: fields.py:44 96 | msgid "Hidden" 97 | msgstr "Oculto" 98 | 99 | #: forms.py:50 forms.py:59 forms.py:66 forms.py:75 100 | msgid "Nothing" 101 | msgstr "Nada" 102 | 103 | #: forms.py:51 104 | msgid "Contains" 105 | msgstr "Contém" 106 | 107 | #: forms.py:52 108 | msgid "Doesn't contain" 109 | msgstr "Não contém" 110 | 111 | #: forms.py:53 112 | msgid "Equals" 113 | msgstr "Igual" 114 | 115 | #: forms.py:54 116 | msgid "Doesn't equal" 117 | msgstr "Diferente" 118 | 119 | #: forms.py:60 120 | msgid "Equals any" 121 | msgstr "Igual algum" 122 | 123 | #: forms.py:61 124 | msgid "Doesn't equal any" 125 | msgstr "Diferente de algum" 126 | 127 | #: forms.py:67 128 | msgid "Contains any" 129 | msgstr "Contém algum" 130 | 131 | #: forms.py:68 132 | msgid "Contains all" 133 | msgstr "Contém todos" 134 | 135 | #: forms.py:69 136 | msgid "Doesn't contain any" 137 | msgstr "Não contém nenhum" 138 | 139 | #: forms.py:70 140 | msgid "Doesn't contain all" 141 | msgstr "Não contém todos" 142 | 143 | #: forms.py:76 144 | msgid "Is between" 145 | msgstr "Está entre" 146 | 147 | #: forms.py:270 148 | msgid "Checked" 149 | msgstr "Selecionado" 150 | 151 | #: forms.py:270 152 | msgid "Not checked" 153 | msgstr "Não selecionado" 154 | 155 | #: forms.py:293 forms.py:308 156 | msgid "and" 157 | msgstr "e" 158 | 159 | #: models.py:17 160 | msgid "Draft" 161 | msgstr "Rascunho" 162 | 163 | #: models.py:18 164 | msgid "Published" 165 | msgstr "Publicado" 166 | 167 | #: models.py:54 168 | msgid "Title" 169 | msgstr "Título" 170 | 171 | #: models.py:57 172 | msgid "Intro" 173 | msgstr "Introdução" 174 | 175 | #: models.py:58 176 | msgid "Button text" 177 | msgstr "Texto do botão" 178 | 179 | #: models.py:59 180 | msgid "Submit" 181 | msgstr "Enviar" 182 | 183 | #: models.py:60 184 | msgid "Response" 185 | msgstr "Resposta" 186 | 187 | #: models.py:61 188 | msgid "Status" 189 | msgstr "Status" 190 | 191 | #: models.py:63 192 | msgid "Published from" 193 | msgstr "Publicado em" 194 | 195 | #: models.py:64 196 | msgid "With published selected, won't be shown until this time" 197 | msgstr "Com a opção publicado selecionada, não será exibido antes desta data/hora" 198 | 199 | #: models.py:66 200 | msgid "Expires on" 201 | msgstr "Expira em" 202 | 203 | #: models.py:67 204 | msgid "With published selected, won't be shown after this time" 205 | msgstr "Com a opção publicado selecionada, não será exibido após esta data/hora" 206 | 207 | #: models.py:69 208 | msgid "Login required" 209 | msgstr "Login necessário" 210 | 211 | #: models.py:70 212 | msgid "If checked, only logged in users can view the form" 213 | msgstr "Se selecionado, somente usuários logados poderão ver o formulário" 214 | 215 | #: models.py:71 216 | msgid "Send email" 217 | msgstr "Enviar e-mail" 218 | 219 | #: models.py:72 220 | msgid "If checked, the person entering the form will be sent an email" 221 | msgstr "Se selecionado, a pessoa receberá um e-mail quando o formulário for submetido" 222 | 223 | #: models.py:73 224 | msgid "From address" 225 | msgstr "Endereço do remetente" 226 | 227 | #: models.py:74 228 | msgid "The address the email will be sent from" 229 | msgstr "Endereço de e-mail a partir do qual será enviado" 230 | 231 | #: models.py:75 232 | msgid "Send copies to" 233 | msgstr "Enviar cópias para" 234 | 235 | #: models.py:76 236 | msgid "One or more email addresses, separated by commas" 237 | msgstr "Um ou mais endereços de e-mail, separados por vírgulas" 238 | 239 | #: models.py:78 240 | msgid "Subject" 241 | msgstr "Assunto" 242 | 243 | #: models.py:79 244 | msgid "Message" 245 | msgstr "Mensagem" 246 | 247 | #: models.py:84 248 | msgid "Form" 249 | msgstr "Formulário" 250 | 251 | #: models.py:85 252 | msgid "Forms" 253 | msgstr "Formulários" 254 | 255 | #: models.py:116 256 | msgid "View form on site" 257 | msgstr "Visualizar formulário" 258 | 259 | #: models.py:117 260 | msgid "Filter entries" 261 | msgstr "Filtrar entradas" 262 | 263 | #: models.py:118 264 | msgid "View all entries" 265 | msgstr "Ver todas as entradas" 266 | 267 | #: models.py:119 268 | msgid "Export all entries" 269 | msgstr "Exportar todas as entradas" 270 | 271 | #: models.py:141 272 | msgid "Label" 273 | msgstr "Legenda" 274 | 275 | #: models.py:144 276 | msgid "Type" 277 | msgstr "Tipo" 278 | 279 | #: models.py:145 280 | msgid "Required" 281 | msgstr "Exigido" 282 | 283 | #: models.py:146 284 | msgid "Visible" 285 | msgstr "Visível" 286 | 287 | #: models.py:147 288 | msgid "Choices" 289 | msgstr "Opções" 290 | 291 | #: models.py:152 292 | msgid "Default value" 293 | msgstr "Valor padrão" 294 | 295 | #: models.py:154 296 | msgid "Placeholder Text" 297 | msgstr "Texto do placeholder" 298 | 299 | #: models.py:156 300 | msgid "Help text" 301 | msgstr "Texto de ajuda" 302 | 303 | #: models.py:161 templates/admin/forms/entries.html:87 304 | msgid "Field" 305 | msgstr "Campo" 306 | 307 | #: models.py:162 308 | msgid "Fields" 309 | msgstr "Campos" 310 | 311 | #: models.py:213 312 | msgid "Form entry" 313 | msgstr "Entrada do formulário" 314 | 315 | #: models.py:214 316 | msgid "Form entries" 317 | msgstr "Entradas do formulário" 318 | 319 | #: models.py:228 320 | msgid "Form field entry" 321 | msgstr "Entrada do campo do formulário" 322 | 323 | #: models.py:229 324 | msgid "Form field entries" 325 | msgstr "Entradas do campo do formulário" 326 | 327 | #: models.py:257 328 | msgid "Order" 329 | msgstr "Ordem" 330 | 331 | #: templates/admin/forms/change_form.html:10 332 | #: templates/admin/forms/entries.html:116 333 | msgid "View entries" 334 | msgstr "Ver entradas" 335 | 336 | #: templates/admin/forms/change_form.html:13 337 | msgid "History" 338 | msgstr "Histórico" 339 | 340 | #: templates/admin/forms/change_form.html:17 341 | msgid "View on site" 342 | msgstr "Ver no site" 343 | 344 | #: templates/admin/forms/entries.html:61 345 | msgid "No entries selected" 346 | msgstr "Não há entradas selecionadas" 347 | 348 | #: templates/admin/forms/entries.html:64 349 | msgid "Delete selected entries?" 350 | msgstr "Apagar as entradas selecionadas?" 351 | 352 | #: templates/admin/forms/entries.html:73 353 | msgid "Home" 354 | msgstr "Home" 355 | 356 | #: templates/admin/forms/entries.html:88 357 | msgid "Include" 358 | msgstr "Incluir" 359 | 360 | #: templates/admin/forms/entries.html:89 361 | msgid "Filter by" 362 | msgstr "Filtrar por" 363 | 364 | #: templates/admin/forms/entries.html:107 365 | msgid "All" 366 | msgstr "Todos" 367 | 368 | #: templates/admin/forms/entries.html:115 369 | #: templates/admin/forms/entries.html:158 370 | msgid "Back to form" 371 | msgstr "Voltar para o formulário" 372 | 373 | #: templates/admin/forms/entries.html:117 374 | msgid "Export CSV" 375 | msgstr "Exportar CSV" 376 | 377 | #: templates/admin/forms/entries.html:119 378 | msgid "Export XLS" 379 | msgstr "Exportar XLS" 380 | 381 | #: templates/admin/forms/entries.html:123 382 | msgid "Entries" 383 | msgstr "Entradas" 384 | 385 | #: templates/admin/forms/entries.html:159 386 | msgid "Delete selected" 387 | msgstr "Apagar selecionado" 388 | 389 | #: templates/admin/forms/entries.html:163 390 | msgid "No entries to display" 391 | msgstr "Não há entradas para exibir" 392 | -------------------------------------------------------------------------------- /forms_builder/forms/admin.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from future.builtins import bytes, open 3 | 4 | from csv import writer 5 | from mimetypes import guess_type 6 | from os.path import join 7 | from datetime import datetime 8 | from io import BytesIO, StringIO 9 | 10 | from django.conf.urls import url 11 | from django.contrib import admin 12 | from django.core.files.storage import FileSystemStorage 13 | from django.core.urlresolvers import reverse 14 | from django.db.models import Count 15 | from django.http import HttpResponse, HttpResponseRedirect 16 | from django.shortcuts import render, get_object_or_404 17 | from django.utils.translation import ungettext, ugettext_lazy as _ 18 | 19 | from forms_builder.forms.forms import EntriesForm 20 | from forms_builder.forms.models import Form, Field, FormEntry, FieldEntry 21 | from forms_builder.forms.settings import CSV_DELIMITER, UPLOAD_ROOT 22 | from forms_builder.forms.settings import USE_SITES, EDITABLE_SLUGS 23 | from forms_builder.forms.utils import now, slugify 24 | 25 | try: 26 | import xlwt 27 | XLWT_INSTALLED = True 28 | XLWT_DATETIME_STYLE = xlwt.easyxf(num_format_str='MM/DD/YYYY HH:MM:SS') 29 | except ImportError: 30 | XLWT_INSTALLED = False 31 | 32 | 33 | fs = FileSystemStorage(location=UPLOAD_ROOT) 34 | form_admin_filter_horizontal = () 35 | form_admin_fieldsets = [ 36 | (None, {"fields": ("title", ("status", "login_required",), 37 | ("publish_date", "expiry_date",), 38 | "intro", "button_text", "response", "redirect_url")}), 39 | (_("Email"), {"fields": ("send_email", "email_from", "email_copies", 40 | "email_subject", "email_message")}),] 41 | 42 | if EDITABLE_SLUGS: 43 | form_admin_fieldsets.append( 44 | (_("Slug"), {"fields": ("slug",), "classes": ("collapse",)})) 45 | 46 | if USE_SITES: 47 | form_admin_fieldsets.append((_("Sites"), {"fields": ("sites",), 48 | "classes": ("collapse",)})) 49 | form_admin_filter_horizontal = ("sites",) 50 | 51 | 52 | class FieldAdmin(admin.TabularInline): 53 | model = Field 54 | exclude = ('slug', ) 55 | 56 | 57 | class FormAdmin(admin.ModelAdmin): 58 | formentry_model = FormEntry 59 | fieldentry_model = FieldEntry 60 | 61 | inlines = (FieldAdmin,) 62 | list_display = ("title", "status", "email_copies", "publish_date", 63 | "expiry_date", "total_entries", "admin_links") 64 | list_display_links = ("title",) 65 | list_editable = ("status", "email_copies", "publish_date", "expiry_date") 66 | list_filter = ("status",) 67 | filter_horizontal = form_admin_filter_horizontal 68 | search_fields = ("title", "intro", "response", "email_from", 69 | "email_copies") 70 | radio_fields = {"status": admin.HORIZONTAL} 71 | fieldsets = form_admin_fieldsets 72 | 73 | def get_queryset(self, request): 74 | """ 75 | Annotate the queryset with the entries count for use in the 76 | admin list view. 77 | """ 78 | qs = super(FormAdmin, self).get_queryset(request) 79 | return qs.annotate(total_entries=Count("entries")) 80 | 81 | def get_urls(self): 82 | """ 83 | Add the entries view to urls. 84 | """ 85 | urls = super(FormAdmin, self).get_urls() 86 | extra_urls = [ 87 | url("^(?P\d+)/entries/$", 88 | self.admin_site.admin_view(self.entries_view), 89 | name="form_entries"), 90 | url("^(?P\d+)/entries/show/$", 91 | self.admin_site.admin_view(self.entries_view), 92 | {"show": True}, name="form_entries_show"), 93 | url("^(?P\d+)/entries/export/$", 94 | self.admin_site.admin_view(self.entries_view), 95 | {"export": True}, name="form_entries_export"), 96 | url("^file/(?P\d+)/$", 97 | self.admin_site.admin_view(self.file_view), 98 | name="form_file"), 99 | ] 100 | return extra_urls + urls 101 | 102 | def entries_view(self, request, form_id, show=False, export=False, 103 | export_xls=False): 104 | """ 105 | Displays the form entries in a HTML table with option to 106 | export as CSV file. 107 | """ 108 | if request.POST.get("back"): 109 | bits = (self.model._meta.app_label, self.model.__name__.lower()) 110 | change_url = reverse("admin:%s_%s_change" % bits, args=(form_id,)) 111 | return HttpResponseRedirect(change_url) 112 | form = get_object_or_404(self.model, id=form_id) 113 | post = request.POST or None 114 | args = form, request, self.formentry_model, self.fieldentry_model, post 115 | entries_form = EntriesForm(*args) 116 | delete = "%s.delete_formentry" % self.formentry_model._meta.app_label 117 | can_delete_entries = request.user.has_perm(delete) 118 | submitted = entries_form.is_valid() or show or export or export_xls 119 | export = export or request.POST.get("export") 120 | export_xls = export_xls or request.POST.get("export_xls") 121 | if submitted: 122 | if export: 123 | response = HttpResponse(content_type="text/csv") 124 | fname = "%s-%s.csv" % (form.slug, slugify(now().ctime())) 125 | attachment = "attachment; filename=%s" % fname 126 | response["Content-Disposition"] = attachment 127 | queue = StringIO() 128 | try: 129 | csv = writer(queue, delimiter=CSV_DELIMITER) 130 | writerow = csv.writerow 131 | except TypeError: 132 | queue = BytesIO() 133 | delimiter = bytes(CSV_DELIMITER, encoding="utf-8") 134 | csv = writer(queue, delimiter=delimiter) 135 | writerow = lambda row: csv.writerow([c.encode("utf-8") 136 | if hasattr(c, "encode") else c for c in row]) 137 | writerow(entries_form.columns()) 138 | for row in entries_form.rows(csv=True): 139 | writerow(row) 140 | data = queue.getvalue() 141 | response.write(data) 142 | return response 143 | elif XLWT_INSTALLED and export_xls: 144 | response = HttpResponse(content_type="application/vnd.ms-excel") 145 | fname = "%s-%s.xls" % (form.slug, slugify(now().ctime())) 146 | attachment = "attachment; filename=%s" % fname 147 | response["Content-Disposition"] = attachment 148 | queue = BytesIO() 149 | workbook = xlwt.Workbook(encoding='utf8') 150 | sheet = workbook.add_sheet(form.title[:31]) 151 | for c, col in enumerate(entries_form.columns()): 152 | sheet.write(0, c, col) 153 | for r, row in enumerate(entries_form.rows(csv=True)): 154 | for c, item in enumerate(row): 155 | if isinstance(item, datetime): 156 | item = item.replace(tzinfo=None) 157 | sheet.write(r + 2, c, item, XLWT_DATETIME_STYLE) 158 | else: 159 | sheet.write(r + 2, c, item) 160 | workbook.save(queue) 161 | data = queue.getvalue() 162 | response.write(data) 163 | return response 164 | elif request.POST.get("delete") and can_delete_entries: 165 | selected = request.POST.getlist("selected") 166 | if selected: 167 | try: 168 | from django.contrib.messages import info 169 | except ImportError: 170 | def info(request, message, fail_silently=True): 171 | request.user.message_set.create(message=message) 172 | entries = self.formentry_model.objects.filter(id__in=selected) 173 | count = entries.count() 174 | if count > 0: 175 | entries.delete() 176 | message = ungettext("1 entry deleted", 177 | "%(count)s entries deleted", count) 178 | info(request, message % {"count": count}) 179 | template = "admin/forms/entries.html" 180 | context = {"title": _("View Entries"), "entries_form": entries_form, 181 | "opts": self.model._meta, "original": form, 182 | "can_delete_entries": can_delete_entries, 183 | "submitted": submitted, 184 | "xlwt_installed": XLWT_INSTALLED} 185 | return render(request, template, context) 186 | 187 | def file_view(self, request, field_entry_id): 188 | """ 189 | Output the file for the requested field entry. 190 | """ 191 | model = self.fieldentry_model 192 | field_entry = get_object_or_404(model, id=field_entry_id) 193 | path = join(fs.location, field_entry.value) 194 | response = HttpResponse(content_type=guess_type(path)[0]) 195 | f = open(path, "r+b") 196 | response["Content-Disposition"] = "attachment; filename=%s" % f.name 197 | response.write(f.read()) 198 | f.close() 199 | return response 200 | 201 | 202 | admin.site.register(Form, FormAdmin) 203 | -------------------------------------------------------------------------------- /forms_builder/forms/locale/pl/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: pl\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2014-07-18 21:02+0200\n" 11 | "PO-Revision-Date: 2014-07-18 23:05+0100\n" 12 | "Last-Translator: Kamil Dębowski \n" 13 | "Language-Team: Kamil Dębowski \n" 14 | "Language: pl\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " 19 | "|| n%100>=20) ? 1 : 2);\n" 20 | "X-Generator: Poedit 1.6.6\n" 21 | 22 | #: .\admin.py:40 .\fields.py:33 23 | msgid "Email" 24 | msgstr "Email" 25 | 26 | #: .\admin.py:45 .\models.py:60 .\models.py:151 27 | msgid "Slug" 28 | msgstr "Slug" 29 | 30 | #: .\admin.py:48 31 | msgid "Sites" 32 | msgstr "Strony" 33 | 34 | #: .\admin.py:177 35 | #, python-format 36 | msgid "1 entry deleted" 37 | msgid_plural "%(count)s entries deleted" 38 | msgstr[0] "1 wpis usunięty" 39 | msgstr[1] "%(count)s wpisy usunięte" 40 | msgstr[2] "%(count)s wpisów usuniętych" 41 | 42 | #: .\admin.py:181 43 | msgid "View Entries" 44 | msgstr "Zobacz wpisy" 45 | 46 | #: .\fields.py:31 47 | msgid "Single line text" 48 | msgstr "Pojedynczy wiersz tekstu" 49 | 50 | #: .\fields.py:32 51 | msgid "Multi line text" 52 | msgstr "Wiele linii tekstu" 53 | 54 | #: .\fields.py:34 55 | msgid "Number" 56 | msgstr "Liczba" 57 | 58 | #: .\fields.py:35 59 | msgid "URL" 60 | msgstr "URL" 61 | 62 | #: .\fields.py:36 63 | msgid "Check box" 64 | msgstr "Pole wyboru" 65 | 66 | #: .\fields.py:37 67 | msgid "Check boxes" 68 | msgstr "Pola wyboru" 69 | 70 | #: .\fields.py:38 71 | msgid "Drop down" 72 | msgstr "Lista rozwijana" 73 | 74 | #: .\fields.py:39 75 | msgid "Multi select" 76 | msgstr "Wielokrotny wybór" 77 | 78 | #: .\fields.py:40 79 | msgid "Radio buttons" 80 | msgstr "Jednokrotny wybór" 81 | 82 | #: .\fields.py:41 83 | msgid "File upload" 84 | msgstr "Przesłanie pliku" 85 | 86 | #: .\fields.py:42 87 | msgid "Date" 88 | msgstr "Data" 89 | 90 | #: .\fields.py:43 .\models.py:219 91 | msgid "Date/time" 92 | msgstr "Data/czas" 93 | 94 | #: .\fields.py:44 95 | msgid "Date of birth" 96 | msgstr "Data urodzenia" 97 | 98 | #: .\fields.py:45 99 | msgid "Hidden" 100 | msgstr "Ukryte" 101 | 102 | #: .\forms.py:52 .\forms.py:61 .\forms.py:68 .\forms.py:77 103 | msgid "Nothing" 104 | msgstr "Nic" 105 | 106 | #: .\forms.py:53 107 | msgid "Contains" 108 | msgstr "Zawiera" 109 | 110 | #: .\forms.py:54 111 | msgid "Doesn't contain" 112 | msgstr "Nie zawiera" 113 | 114 | #: .\forms.py:55 115 | msgid "Equals" 116 | msgstr "Równy" 117 | 118 | #: .\forms.py:56 119 | msgid "Doesn't equal" 120 | msgstr "Nieówny" 121 | 122 | #: .\forms.py:62 123 | msgid "Equals any" 124 | msgstr "Równy któremukolwiek" 125 | 126 | #: .\forms.py:63 127 | msgid "Doesn't equal any" 128 | msgstr "Nierówny któremukolwiek" 129 | 130 | #: .\forms.py:69 131 | msgid "Contains any" 132 | msgstr "Zawiera jakiekolwiek" 133 | 134 | #: .\forms.py:70 135 | msgid "Contains all" 136 | msgstr "Zawiera wszystkie" 137 | 138 | #: .\forms.py:71 139 | msgid "Doesn't contain any" 140 | msgstr "Niezawiera któregokolwiek" 141 | 142 | #: .\forms.py:72 143 | msgid "Doesn't contain all" 144 | msgstr "Nie zawiera wszystkich" 145 | 146 | #: .\forms.py:78 147 | msgid "Is between" 148 | msgstr "Jest pomiędzy" 149 | 150 | #: .\forms.py:272 151 | msgid "Checked" 152 | msgstr "Zaznaczony" 153 | 154 | #: .\forms.py:272 155 | msgid "Not checked" 156 | msgstr "Niezaznaczony" 157 | 158 | #: .\forms.py:295 .\forms.py:310 159 | msgid "and" 160 | msgstr "i" 161 | 162 | #: .\models.py:21 163 | msgid "Draft" 164 | msgstr "Szkic" 165 | 166 | #: .\models.py:22 167 | msgid "Published" 168 | msgstr "Opublikowany" 169 | 170 | #: .\models.py:59 171 | msgid "Title" 172 | msgstr "Tytuł" 173 | 174 | #: .\models.py:62 175 | msgid "Intro" 176 | msgstr "Wstęp" 177 | 178 | #: .\models.py:63 179 | msgid "Button text" 180 | msgstr "Tekst przycisku" 181 | 182 | #: .\models.py:64 183 | msgid "Submit" 184 | msgstr "Wyślij" 185 | 186 | #: .\models.py:65 187 | msgid "Response" 188 | msgstr "Odpowiedź" 189 | 190 | #: .\models.py:66 191 | msgid "Redirect url" 192 | msgstr "Adres do przekierowania" 193 | 194 | #: .\models.py:68 195 | msgid "An alternate URL to redirect to after form submission" 196 | msgstr "Alternatywny adres do przekierowania po wysłaniu formularza" 197 | 198 | #: .\models.py:69 199 | msgid "Status" 200 | msgstr "Status" 201 | 202 | #: .\models.py:71 203 | msgid "Published from" 204 | msgstr "Opublikowany od" 205 | 206 | #: .\models.py:72 207 | msgid "With published selected, won't be shown until this time" 208 | msgstr "Jeśli opublikowany, nie będzie widoczny przed tym czasem" 209 | 210 | #: .\models.py:74 211 | msgid "Expires on" 212 | msgstr "Wygasa" 213 | 214 | #: .\models.py:75 215 | msgid "With published selected, won't be shown after this time" 216 | msgstr "Jeśli opublikowany, nie będzie widoczny po tym czasie" 217 | 218 | #: .\models.py:77 219 | msgid "Login required" 220 | msgstr "Wymaga logowania" 221 | 222 | #: .\models.py:78 223 | msgid "If checked, only logged in users can view the form" 224 | msgstr "Jeśli zaznaczone, tylko zalogowani użytkownicy zobaczą formularz" 225 | 226 | #: .\models.py:79 227 | msgid "Send email" 228 | msgstr "Wyślij email" 229 | 230 | #: .\models.py:80 231 | msgid "If checked, the person entering the form will be sent an email" 232 | msgstr "Jeśli zaznaczone, osoba wysyłająca formularz otrzyma email" 233 | 234 | #: .\models.py:81 235 | msgid "From address" 236 | msgstr "Z adresu" 237 | 238 | #: .\models.py:82 239 | msgid "The address the email will be sent from" 240 | msgstr "Adres, z którego będzie wysłany formularz" 241 | 242 | #: .\models.py:83 243 | msgid "Send copies to" 244 | msgstr "Wyślij kopie do" 245 | 246 | #: .\models.py:84 247 | msgid "One or more email addresses, separated by commas" 248 | msgstr "Jeden lub więcej adresów email, oddzielonych przecinkami" 249 | 250 | #: .\models.py:86 251 | msgid "Subject" 252 | msgstr "Temat" 253 | 254 | #: .\models.py:87 255 | msgid "Message" 256 | msgstr "Wiadomość" 257 | 258 | #: .\models.py:92 259 | msgid "Form" 260 | msgstr "Formularz" 261 | 262 | #: .\models.py:93 263 | msgid "Forms" 264 | msgstr "Formularze" 265 | 266 | #: .\models.py:124 267 | msgid "View form on site" 268 | msgstr "Zobacz formularz na stronie" 269 | 270 | #: .\models.py:125 271 | msgid "Filter entries" 272 | msgstr "Filtruj wpisy" 273 | 274 | #: .\models.py:126 275 | msgid "View all entries" 276 | msgstr "Zobacz wszystkie wpisy" 277 | 278 | #: .\models.py:127 279 | msgid "Export all entries" 280 | msgstr "Eksportuj wszystkie wpisy" 281 | 282 | #: .\models.py:150 283 | msgid "Label" 284 | msgstr "Etykieta" 285 | 286 | #: .\models.py:153 287 | msgid "Type" 288 | msgstr "Typ" 289 | 290 | #: .\models.py:154 291 | msgid "Required" 292 | msgstr "Wymagane" 293 | 294 | #: .\models.py:155 295 | msgid "Visible" 296 | msgstr "Widoczne" 297 | 298 | #: .\models.py:156 299 | msgid "Choices" 300 | msgstr "Opcje wyboru" 301 | 302 | #: .\models.py:161 303 | msgid "Default value" 304 | msgstr "Wartość domyślna" 305 | 306 | #: .\models.py:163 307 | msgid "Placeholder Text" 308 | msgstr "Placeholder" 309 | 310 | #: .\models.py:165 311 | msgid "Help text" 312 | msgstr "Tekst pomocniczy" 313 | 314 | #: .\models.py:170 .\templates\admin\forms\entries.html:88 315 | msgid "Field" 316 | msgstr "Pole" 317 | 318 | #: .\models.py:171 319 | msgid "Fields" 320 | msgstr "Pola" 321 | 322 | #: .\models.py:222 323 | msgid "Form entry" 324 | msgstr "Wpis formularza" 325 | 326 | #: .\models.py:223 327 | msgid "Form entries" 328 | msgstr "Wpisy formularza" 329 | 330 | #: .\models.py:237 331 | msgid "Form field entry" 332 | msgstr "Wpis pola formularza" 333 | 334 | #: .\models.py:238 335 | msgid "Form field entries" 336 | msgstr "Wpisy pola formularza" 337 | 338 | #: .\models.py:266 339 | msgid "Order" 340 | msgstr "Porządek" 341 | 342 | #: .\templates\admin\forms\change_form.html:10 343 | #: .\templates\admin\forms\entries.html:117 344 | msgid "View entries" 345 | msgstr "Zobacz wpisy" 346 | 347 | #: .\templates\admin\forms\change_form.html:13 348 | msgid "History" 349 | msgstr "Historia" 350 | 351 | #: .\templates\admin\forms\change_form.html:17 352 | msgid "View on site" 353 | msgstr "Zobacz na stronie" 354 | 355 | #: .\templates\admin\forms\entries.html:62 356 | msgid "No entries selected" 357 | msgstr "Brak zaznaczonych wpisów" 358 | 359 | #: .\templates\admin\forms\entries.html:65 360 | msgid "Delete selected entries?" 361 | msgstr "Usunąć zaznaczone wpisy?" 362 | 363 | #: .\templates\admin\forms\entries.html:74 364 | msgid "Home" 365 | msgstr "Strona główna" 366 | 367 | #: .\templates\admin\forms\entries.html:89 368 | msgid "Include" 369 | msgstr "Uwzględnij" 370 | 371 | #: .\templates\admin\forms\entries.html:90 372 | msgid "Filter by" 373 | msgstr "Filtruj używając" 374 | 375 | #: .\templates\admin\forms\entries.html:108 376 | msgid "All" 377 | msgstr "Wszystko" 378 | 379 | #: .\templates\admin\forms\entries.html:116 380 | #: .\templates\admin\forms\entries.html:157 381 | msgid "Back to form" 382 | msgstr "Wróć do formlarza" 383 | 384 | #: .\templates\admin\forms\entries.html:118 385 | msgid "Export CSV" 386 | msgstr "Eksportuj do CSV" 387 | 388 | #: .\templates\admin\forms\entries.html:120 389 | msgid "Export XLS" 390 | msgstr "Eksportuj do XLS" 391 | 392 | #: .\templates\admin\forms\entries.html:124 393 | msgid "Entries" 394 | msgstr "Wpisy" 395 | 396 | #: .\templates\admin\forms\entries.html:158 397 | msgid "Delete selected" 398 | msgstr "Usuń zaznaczone" 399 | 400 | #: .\templates\admin\forms\entries.html:162 401 | msgid "No entries to display" 402 | msgstr "Brak wpisów do wyświetlenia" 403 | -------------------------------------------------------------------------------- /forms_builder/forms/locale/nl/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Dutch Translation for stephenmcd's django-forms-builder 2 | # Copyright (C) 2010 THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # Wouter van der Graaf , 2010. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2015-07-16 15:08+0200\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: Fluxility \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 20 | 21 | #: admin.py:40 fields.py:33 22 | msgid "Email" 23 | msgstr "E-mail" 24 | 25 | #: admin.py:45 models.py:59 models.py:166 26 | msgid "Slug" 27 | msgstr "Slug" 28 | 29 | #: admin.py:48 30 | msgid "Sites" 31 | msgstr "Sites" 32 | 33 | #: admin.py:177 34 | #, python-format 35 | msgid "1 entry deleted" 36 | msgid_plural "%(count)s entries deleted" 37 | msgstr[0] "1 inzending verwijderd" 38 | msgstr[1] "%(count)s inzendingen verwijderd" 39 | 40 | #: admin.py:181 41 | #| msgid "View on site" 42 | msgid "View Entries" 43 | msgstr "Bekijk op site" 44 | 45 | #: fields.py:31 46 | msgid "Single line text" 47 | msgstr "Enkele rij tekst" 48 | 49 | #: fields.py:32 50 | msgid "Multi line text" 51 | msgstr "Meerdere rijen tekst" 52 | 53 | #: fields.py:34 54 | msgid "Number" 55 | msgstr "Nummer" 56 | 57 | #: fields.py:35 58 | msgid "URL" 59 | msgstr "URL" 60 | 61 | #: fields.py:36 62 | msgid "Check box" 63 | msgstr "Aankruisvakje" 64 | 65 | #: fields.py:37 66 | msgid "Check boxes" 67 | msgstr "Aankruisvakjes" 68 | 69 | #: fields.py:38 70 | msgid "Drop down" 71 | msgstr "Uitklapveld" 72 | 73 | #: fields.py:39 74 | msgid "Multi select" 75 | msgstr "Lijstveld" 76 | 77 | #: fields.py:40 78 | msgid "Radio buttons" 79 | msgstr "Keuzerondjes" 80 | 81 | #: fields.py:41 82 | msgid "File upload" 83 | msgstr "Bestand upload" 84 | 85 | #: fields.py:42 86 | msgid "Date" 87 | msgstr "Datum" 88 | 89 | #: fields.py:43 models.py:234 90 | msgid "Date/time" 91 | msgstr "Datum/tijd" 92 | 93 | #: fields.py:44 94 | msgid "Date of birth" 95 | msgstr "Geboortedatum" 96 | 97 | #: fields.py:45 98 | msgid "Hidden" 99 | msgstr "Verborgen" 100 | 101 | #: forms.py:52 forms.py:61 forms.py:68 forms.py:77 102 | msgid "Nothing" 103 | msgstr "Niets" 104 | 105 | #: forms.py:53 106 | msgid "Contains" 107 | msgstr "Bevat" 108 | 109 | #: forms.py:54 110 | msgid "Doesn't contain" 111 | msgstr "Bevat niet" 112 | 113 | #: forms.py:55 114 | msgid "Equals" 115 | msgstr "Gelijk aan" 116 | 117 | #: forms.py:56 118 | msgid "Doesn't equal" 119 | msgstr "Niet gelijk aan" 120 | 121 | #: forms.py:62 122 | msgid "Equals any" 123 | msgstr "Gelijk aan één van" 124 | 125 | #: forms.py:63 126 | msgid "Doesn't equal any" 127 | msgstr "Niet gelijk aan één van" 128 | 129 | #: forms.py:69 130 | msgid "Contains any" 131 | msgstr "Bevat minstens één van" 132 | 133 | #: forms.py:70 134 | msgid "Contains all" 135 | msgstr "Bevat alle van" 136 | 137 | #: forms.py:71 138 | msgid "Doesn't contain any" 139 | msgstr "Bevat geen enkele van" 140 | 141 | #: forms.py:72 142 | msgid "Doesn't contain all" 143 | msgstr "Bevat niet alle van" 144 | 145 | #: forms.py:78 146 | msgid "Is between" 147 | msgstr "Is tussen" 148 | 149 | #: forms.py:276 150 | #| msgid "Check boxes" 151 | msgid "Checked" 152 | msgstr "Aangevinkt" 153 | 154 | #: forms.py:276 155 | msgid "Not checked" 156 | msgstr "Niet aangevinkt" 157 | 158 | #: forms.py:299 forms.py:314 159 | msgid "and" 160 | msgstr "en" 161 | 162 | #: models.py:20 163 | msgid "Draft" 164 | msgstr "Opzet" 165 | 166 | #: models.py:21 167 | #| msgid "Published from" 168 | msgid "Published" 169 | msgstr "Gepubliceerd" 170 | 171 | #: models.py:58 172 | msgid "Title" 173 | msgstr "Titel" 174 | 175 | #: models.py:61 176 | msgid "Intro" 177 | msgstr "Intro" 178 | 179 | #: models.py:62 180 | msgid "Button text" 181 | msgstr "Knoptekst" 182 | 183 | #: models.py:63 184 | msgid "Submit" 185 | msgstr "Verzenden" 186 | 187 | #: models.py:64 188 | msgid "Response" 189 | msgstr "Respons" 190 | 191 | #: models.py:65 192 | msgid "Redirect url" 193 | msgstr "Doorverwijs URL" 194 | 195 | #: models.py:67 196 | msgid "An alternate URL to redirect to after form submission" 197 | msgstr "Een URL om de gebruiker naar door te verwijzen nadat hij het" 198 | " formulier heeft verstuurd" 199 | 200 | #: models.py:68 201 | msgid "Status" 202 | msgstr "Status" 203 | 204 | #: models.py:70 205 | msgid "Published from" 206 | msgstr "Gepubliceerd vanaf" 207 | 208 | #: models.py:71 209 | msgid "With published selected, won't be shown until this time" 210 | msgstr "" 211 | "Met gepubliceerd geselecteerd, wordt deze niet getoond tot dit tijdstip" 212 | 213 | #: models.py:73 214 | msgid "Expires on" 215 | msgstr "Vervalt op" 216 | 217 | #: models.py:74 218 | msgid "With published selected, won't be shown after this time" 219 | msgstr "" 220 | "Met gepubliceerd geselecteerd, wordt deze niet getoond vanaf dit tijdstip" 221 | 222 | #: models.py:76 223 | msgid "Login required" 224 | msgstr "Inloggen vereist" 225 | 226 | #: models.py:77 227 | msgid "If checked, only logged in users can view the form" 228 | msgstr "" 229 | "Als aangevinkt, dan kunnen alleen ingelogde gebruikers het formulier zien" 230 | 231 | #: models.py:78 232 | msgid "Send email" 233 | msgstr "Verstuur e-mail" 234 | 235 | #: models.py:79 236 | msgid "If checked, the person entering the form will be sent an email" 237 | msgstr "" 238 | "Als aangevinkt, dan krijgt de persoon die het formulier heeft ingevuld een e-" 239 | "mail" 240 | 241 | #: models.py:80 242 | msgid "From address" 243 | msgstr "Van adres" 244 | 245 | #: models.py:81 246 | msgid "The address the email will be sent from" 247 | msgstr "Het adres waarvandaan de e-mail verzonden wordt" 248 | 249 | #: models.py:82 250 | msgid "Send copies to" 251 | msgstr "Stuur kopie naar" 252 | 253 | #: models.py:83 254 | msgid "One or more email addresses, separated by commas" 255 | msgstr "Een of meer e-mailadressen, gescheiden door komma's" 256 | 257 | #: models.py:85 258 | msgid "Subject" 259 | msgstr "Onderwerp" 260 | 261 | #: models.py:86 262 | msgid "Message" 263 | msgstr "Bericht" 264 | 265 | #: models.py:91 266 | msgid "Form" 267 | msgstr "Formulier" 268 | 269 | #: models.py:92 270 | msgid "Forms" 271 | msgstr "Formulieren" 272 | 273 | #: models.py:139 274 | #| msgid "View on site" 275 | msgid "View form on site" 276 | msgstr "Bekijk formulier op site" 277 | 278 | #: models.py:140 279 | #| msgid "Form entries" 280 | msgid "Filter entries" 281 | msgstr "Filter inzendingen" 282 | 283 | #: models.py:141 284 | #| msgid "Form field entries" 285 | msgid "View all entries" 286 | msgstr "Bekijk alle inzendingen" 287 | 288 | #: models.py:142 289 | #| msgid "Export entries" 290 | msgid "Export all entries" 291 | msgstr "Exporteer alle inzendingen" 292 | 293 | #: models.py:165 294 | msgid "Label" 295 | msgstr "Naam" 296 | 297 | #: models.py:168 298 | msgid "Type" 299 | msgstr "Type" 300 | 301 | #: models.py:169 302 | msgid "Required" 303 | msgstr "Verplicht" 304 | 305 | #: models.py:170 306 | msgid "Visible" 307 | msgstr "Zichtbaar" 308 | 309 | #: models.py:171 310 | msgid "Choices" 311 | msgstr "Keuzes" 312 | 313 | #: models.py:176 314 | msgid "Default value" 315 | msgstr "Standaardwaarde" 316 | 317 | #: models.py:178 318 | msgid "Placeholder Text" 319 | msgstr "Voorbeeldtekst" 320 | 321 | #: models.py:180 322 | msgid "Help text" 323 | msgstr "Helptekst" 324 | 325 | #: models.py:185 templates/admin/forms/entries.html:88 326 | msgid "Field" 327 | msgstr "Veld" 328 | 329 | #: models.py:186 330 | msgid "Fields" 331 | msgstr "Velden" 332 | 333 | #: models.py:237 334 | msgid "Form entry" 335 | msgstr "Formulierinzending" 336 | 337 | #: models.py:238 338 | msgid "Form entries" 339 | msgstr "Formulierinzendingen" 340 | 341 | #: models.py:252 342 | msgid "Form field entry" 343 | msgstr "Formulierveldinzending" 344 | 345 | #: models.py:253 346 | msgid "Form field entries" 347 | msgstr "Formulierveldinzendingen" 348 | 349 | #: models.py:281 350 | msgid "Order" 351 | msgstr "Volgorde" 352 | 353 | #: templates/admin/forms/change_form.html:9 354 | #: templates/admin/forms/entries.html:117 355 | #| msgid "Form entries" 356 | msgid "View entries" 357 | msgstr "Bekijk inzendingen" 358 | 359 | #: templates/admin/forms/change_form.html:12 360 | msgid "History" 361 | msgstr "Geschiedenis" 362 | 363 | #: templates/admin/forms/change_form.html:16 364 | msgid "View on site" 365 | msgstr "Bekijk op site" 366 | 367 | #: templates/admin/forms/entries.html:62 368 | msgid "No entries selected" 369 | msgstr "Geen inzendingen geselecteerd" 370 | 371 | #: templates/admin/forms/entries.html:65 372 | msgid "Delete selected entries?" 373 | msgstr "Verwijder geselecteerde inzendingen?" 374 | 375 | #: templates/admin/forms/entries.html:74 376 | msgid "Home" 377 | msgstr "Home" 378 | 379 | #: templates/admin/forms/entries.html:89 380 | msgid "Include" 381 | msgstr "Toon" 382 | 383 | #: templates/admin/forms/entries.html:90 384 | msgid "Filter by" 385 | msgstr "Filter op" 386 | 387 | #: templates/admin/forms/entries.html:108 388 | msgid "All" 389 | msgstr "Alle" 390 | 391 | #: templates/admin/forms/entries.html:116 392 | #: templates/admin/forms/entries.html:157 393 | msgid "Back to form" 394 | msgstr "Terug naar formulier" 395 | 396 | #: templates/admin/forms/entries.html:118 397 | msgid "Export CSV" 398 | msgstr "Exporteer CSV" 399 | 400 | #: templates/admin/forms/entries.html:120 401 | msgid "Export XLS" 402 | msgstr "Exporteer XLS" 403 | 404 | #: templates/admin/forms/entries.html:124 405 | #| msgid "Export entries" 406 | msgid "Entries" 407 | msgstr "Inzendingen" 408 | 409 | #: templates/admin/forms/entries.html:158 410 | #| msgid "Multi select" 411 | msgid "Delete selected" 412 | msgstr "Verwijder geselecteerde" 413 | 414 | #: templates/admin/forms/entries.html:162 415 | msgid "No entries to display" 416 | msgstr "Geen inzendingen om te tonen" 417 | -------------------------------------------------------------------------------- /forms_builder/forms/models.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from django.contrib.sites.models import Site 4 | from django.core.exceptions import ValidationError 5 | from django.core.urlresolvers import reverse 6 | from django.db import models 7 | from django.db.models import Q 8 | from django.utils.encoding import python_2_unicode_compatible 9 | from django.utils.translation import ugettext, ugettext_lazy as _ 10 | from future.builtins import str 11 | 12 | from forms_builder.forms import fields 13 | from forms_builder.forms import settings 14 | from forms_builder.forms.utils import now, slugify, unique_slug 15 | 16 | 17 | STATUS_DRAFT = 1 18 | STATUS_PUBLISHED = 2 19 | STATUS_CHOICES = ( 20 | (STATUS_DRAFT, _("Draft")), 21 | (STATUS_PUBLISHED, _("Published")), 22 | ) 23 | 24 | 25 | class FormManager(models.Manager): 26 | """ 27 | Only show published forms for non-staff users. 28 | """ 29 | def published(self, for_user=None): 30 | if for_user is not None and for_user.is_staff: 31 | return self.all() 32 | filters = [ 33 | Q(publish_date__lte=now()) | Q(publish_date__isnull=True), 34 | Q(expiry_date__gte=now()) | Q(expiry_date__isnull=True), 35 | Q(status=STATUS_PUBLISHED), 36 | ] 37 | if settings.USE_SITES: 38 | filters.append(Q(sites=Site.objects.get_current())) 39 | return self.filter(*filters) 40 | 41 | 42 | ###################################################################### 43 | # # 44 | # Each of the models are implemented as abstract to allow for # 45 | # subclassing. Default concrete implementations are then defined # 46 | # at the end of this module. # 47 | # # 48 | ###################################################################### 49 | 50 | @python_2_unicode_compatible 51 | class AbstractForm(models.Model): 52 | """ 53 | A user-built form. 54 | """ 55 | 56 | sites = models.ManyToManyField(Site, 57 | default=[settings.SITE_ID], related_name="%(app_label)s_%(class)s_forms") 58 | title = models.CharField(_("Title"), max_length=50) 59 | slug = models.SlugField(_("Slug"), editable=settings.EDITABLE_SLUGS, 60 | max_length=100, unique=True) 61 | intro = models.TextField(_("Intro"), blank=True) 62 | button_text = models.CharField(_("Button text"), max_length=50, 63 | default=_("Submit")) 64 | response = models.TextField(_("Response"), blank=True) 65 | redirect_url = models.CharField(_("Redirect url"), max_length=200, 66 | null=True, blank=True, 67 | help_text=_("An alternate URL to redirect to after form submission")) 68 | status = models.IntegerField(_("Status"), choices=STATUS_CHOICES, 69 | default=STATUS_PUBLISHED) 70 | publish_date = models.DateTimeField(_("Published from"), 71 | help_text=_("With published selected, won't be shown until this time"), 72 | blank=True, null=True) 73 | expiry_date = models.DateTimeField(_("Expires on"), 74 | help_text=_("With published selected, won't be shown after this time"), 75 | blank=True, null=True) 76 | login_required = models.BooleanField(_("Login required"), default=False, 77 | help_text=_("If checked, only logged in users can view the form")) 78 | send_email = models.BooleanField(_("Send email"), default=True, help_text= 79 | _("If checked, the person entering the form will be sent an email")) 80 | email_from = models.EmailField(_("From address"), blank=True, 81 | help_text=_("The address the email will be sent from")) 82 | email_copies = models.CharField(_("Send copies to"), blank=True, 83 | help_text=_("One or more email addresses, separated by commas"), 84 | max_length=200) 85 | email_subject = models.CharField(_("Subject"), max_length=200, blank=True) 86 | email_message = models.TextField(_("Message"), blank=True) 87 | 88 | objects = FormManager() 89 | 90 | class Meta: 91 | verbose_name = _("Form") 92 | verbose_name_plural = _("Forms") 93 | abstract = True 94 | 95 | def __str__(self): 96 | return str(self.title) 97 | 98 | def save(self, *args, **kwargs): 99 | """ 100 | Create a unique slug from title - append an index and increment if it 101 | already exists. 102 | """ 103 | if not self.slug: 104 | slug = slugify(self) 105 | self.slug = unique_slug(self.__class__.objects, "slug", slug) 106 | super(AbstractForm, self).save(*args, **kwargs) 107 | 108 | def published(self, for_user=None): 109 | """ 110 | Mimics the queryset logic in ``FormManager.published``, so we 111 | can check a form is published when it wasn't loaded via the 112 | queryset's ``published`` method, and is passed to the 113 | ``render_built_form`` template tag. 114 | """ 115 | if for_user is not None and for_user.is_staff: 116 | return True 117 | status = self.status == STATUS_PUBLISHED 118 | publish_date = self.publish_date is None or self.publish_date <= now() 119 | expiry_date = self.expiry_date is None or self.expiry_date >= now() 120 | authenticated = for_user is not None and for_user.is_authenticated() 121 | login_required = (not self.login_required or authenticated) 122 | return status and publish_date and expiry_date and login_required 123 | 124 | def total_entries(self): 125 | """ 126 | Called by the admin list view where the queryset is annotated 127 | with the number of entries. 128 | """ 129 | return self.total_entries 130 | total_entries.admin_order_field = "total_entries" 131 | 132 | @models.permalink 133 | def get_absolute_url(self): 134 | return ("form_detail", (), {"slug": self.slug}) 135 | 136 | def admin_links(self): 137 | kw = {"args": (self.id,)} 138 | links = [ 139 | (_("View form on site"), self.get_absolute_url()), 140 | (_("Filter entries"), reverse("admin:form_entries", **kw)), 141 | (_("View all entries"), reverse("admin:form_entries_show", **kw)), 142 | (_("Export all entries"), reverse("admin:form_entries_export", **kw)), 143 | ] 144 | for i, (text, url) in enumerate(links): 145 | links[i] = "%s" % (url, ugettext(text)) 146 | return "
    ".join(links) 147 | admin_links.allow_tags = True 148 | admin_links.short_description = "" 149 | 150 | 151 | class FieldManager(models.Manager): 152 | """ 153 | Only show visible fields when displaying actual form.. 154 | """ 155 | def visible(self): 156 | return self.filter(visible=True) 157 | 158 | 159 | @python_2_unicode_compatible 160 | class AbstractField(models.Model): 161 | """ 162 | A field for a user-built form. 163 | """ 164 | 165 | label = models.CharField(_("Label"), max_length=settings.LABEL_MAX_LENGTH) 166 | slug = models.SlugField(_('Slug'), max_length=100, blank=True, 167 | default="") 168 | field_type = models.IntegerField(_("Type"), choices=fields.NAMES) 169 | required = models.BooleanField(_("Required"), default=True) 170 | visible = models.BooleanField(_("Visible"), default=True) 171 | choices = models.CharField(_("Choices"), max_length=settings.CHOICES_MAX_LENGTH, blank=True, 172 | help_text="Comma separated options where applicable. If an option " 173 | "itself contains commas, surround the option starting with the %s" 174 | "character and ending with the %s character." % 175 | (settings.CHOICES_QUOTE, settings.CHOICES_UNQUOTE)) 176 | default = models.CharField(_("Default value"), blank=True, 177 | max_length=settings.FIELD_MAX_LENGTH) 178 | placeholder_text = models.CharField(_("Placeholder Text"), null=True, 179 | blank=True, max_length=100, editable=settings.USE_HTML5) 180 | help_text = models.CharField(_("Help text"), blank=True, max_length=settings.HELPTEXT_MAX_LENGTH) 181 | 182 | objects = FieldManager() 183 | 184 | class Meta: 185 | verbose_name = _("Field") 186 | verbose_name_plural = _("Fields") 187 | abstract = True 188 | 189 | def __str__(self): 190 | return str(self.label) 191 | 192 | def get_choices(self): 193 | """ 194 | Parse a comma separated choice string into a list of choices taking 195 | into account quoted choices using the ``settings.CHOICES_QUOTE`` and 196 | ``settings.CHOICES_UNQUOTE`` settings. 197 | """ 198 | choice = "" 199 | quoted = False 200 | for char in self.choices: 201 | if not quoted and char == settings.CHOICES_QUOTE: 202 | quoted = True 203 | elif quoted and char == settings.CHOICES_UNQUOTE: 204 | quoted = False 205 | elif char == "," and not quoted: 206 | choice = choice.strip() 207 | if choice: 208 | yield choice, choice 209 | choice = "" 210 | else: 211 | choice += char 212 | choice = choice.strip() 213 | if choice: 214 | yield choice, choice 215 | 216 | def is_a(self, *args): 217 | """ 218 | Helper that returns True if the field's type is given in any arg. 219 | """ 220 | return self.field_type in args 221 | 222 | 223 | class AbstractFormEntry(models.Model): 224 | """ 225 | An entry submitted via a user-built form. 226 | """ 227 | 228 | entry_time = models.DateTimeField(_("Date/time")) 229 | 230 | class Meta: 231 | verbose_name = _("Form entry") 232 | verbose_name_plural = _("Form entries") 233 | abstract = True 234 | 235 | 236 | class AbstractFieldEntry(models.Model): 237 | """ 238 | A single field value for a form entry submitted via a user-built form. 239 | """ 240 | 241 | field_id = models.IntegerField() 242 | value = models.CharField(max_length=settings.FIELD_MAX_LENGTH, 243 | null=True) 244 | 245 | class Meta: 246 | verbose_name = _("Form field entry") 247 | verbose_name_plural = _("Form field entries") 248 | abstract = True 249 | 250 | 251 | ################################################### 252 | # # 253 | # Default concrete implementations are below. # 254 | # # 255 | ################################################### 256 | 257 | class FormEntry(AbstractFormEntry): 258 | form = models.ForeignKey("Form", related_name="entries") 259 | 260 | 261 | class FieldEntry(AbstractFieldEntry): 262 | entry = models.ForeignKey("FormEntry", related_name="fields") 263 | 264 | 265 | class Form(AbstractForm): 266 | pass 267 | 268 | 269 | class Field(AbstractField): 270 | """ 271 | Implements automated field ordering. 272 | """ 273 | 274 | form = models.ForeignKey("Form", related_name="fields") 275 | order = models.IntegerField(_("Order"), null=True, blank=True) 276 | 277 | class Meta(AbstractField.Meta): 278 | ordering = ("order",) 279 | 280 | def save(self, *args, **kwargs): 281 | if self.order is None: 282 | self.order = self.form.fields.count() 283 | if not self.slug: 284 | slug = slugify(self).replace('-', '_') 285 | self.slug = unique_slug(self.form.fields, "slug", slug) 286 | super(Field, self).save(*args, **kwargs) 287 | 288 | def delete(self, *args, **kwargs): 289 | fields_after = self.form.fields.filter(order__gte=self.order) 290 | fields_after.update(order=models.F("order") - 1) 291 | super(Field, self).delete(*args, **kwargs) 292 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. image:: https://secure.travis-ci.org/stephenmcd/django-forms-builder.png?branch=master 2 | :target: http://travis-ci.org/#!/stephenmcd/django-forms-builder 3 | 4 | django-forms-builder 5 | ==================== 6 | 7 | Created by `Stephen McDonald `_ 8 | 9 | A Django reusable app providing the ability for admin users to create 10 | their own forms within the admin interface, drawing from a range of 11 | field widgets such as regular text fields, drop-down lists and file 12 | uploads. Options are also provided for controlling who gets sent email 13 | notifications when a form is submitted. All form entries are made 14 | available in the admin via filterable reporting with CSV/XLS export. 15 | 16 | Form builder: 17 | 18 | .. image:: https://raw.githubusercontent.com/stephenmcd/django-forms-builder/master/docs/img/fields.png 19 | 20 | Data reporting: 21 | 22 | .. image:: https://raw.githubusercontent.com/stephenmcd/django-forms-builder/master/docs/img/report.png 23 | 24 | 25 | HTML5 Features 26 | ============== 27 | 28 | The following HTML5 form features are supported. 29 | 30 | * ``placeholder`` attributes 31 | * ``required`` attributes 32 | * ``email`` fields 33 | * ``date`` fields 34 | * ``datetime`` fields 35 | * ``number`` fields 36 | * ``url`` fields 37 | 38 | 39 | Installation 40 | ============ 41 | 42 | The easiest way to install django-forms-builder is directly from PyPi 43 | using `pip`_ by running the command below:: 44 | 45 | $ pip install -U django-forms-builder 46 | 47 | Otherwise you can download django-forms-builder and install it directly 48 | from source:: 49 | 50 | $ python setup.py install 51 | 52 | Once installed you can configure your project to use 53 | django-forms-builder with the following steps. 54 | 55 | Add ``forms_builder.forms`` to ``INSTALLED_APPS`` in your project's 56 | ``settings`` module:: 57 | 58 | INSTALLED_APPS = ( 59 | # other apps 60 | 'forms_builder.forms', 61 | ) 62 | 63 | If you haven't already, ensure ``django.core.context_processors.request`` 64 | is in the ``TEMPLATE_CONTEXT_PROCESSORS`` setting in your project's 65 | ``settings`` module:: 66 | 67 | TEMPLATE_CONTEXT_PROCESSORS = ( 68 | # other context processors 69 | "django.core.context_processors.request", 70 | # Django 1.6 also needs: 71 | 'django.contrib.auth.context_processors.auth', 72 | ) 73 | 74 | Then add ``forms_builder.forms.urls`` to your project's ``urls`` 75 | module:: 76 | 77 | from django.conf.urls.defaults import patterns, include, url 78 | import forms_builder.forms.urls # add this import 79 | 80 | from django.contrib import admin 81 | admin.autodiscover() 82 | 83 | urlpatterns = patterns('', 84 | # other urlpatterns 85 | url(r'^admin/', include(admin.site.urls)), 86 | url(r'^forms/', include(forms_builder.forms.urls)), 87 | ) 88 | 89 | Finally, sync your database:: 90 | 91 | $ python manage.py syncdb 92 | 93 | As of version 0.5, django-forms-builder provides `South`_ migrations. 94 | If you use south in your project, you'll also need to run migrations:: 95 | 96 | $ python manage.py migrate forms 97 | 98 | 99 | Usage 100 | ===== 101 | 102 | Once installed and configured for your project just go to the admin 103 | page for your project and you will see a new Forms section. In this 104 | you can create and edit forms. Forms are then each viewable with their 105 | own URLs. A template tag ``render_built_form`` is also available for 106 | displaying forms outside of the main form view provided. It will 107 | display a form when given an argument in one of the following 108 | formats, where ``form_instance`` is an instance of the ``Form`` model:: 109 | 110 | {% render_built_form form_instance %} 111 | {% render_built_form form=form_instance %} 112 | {% render_built_form id=form_instance.id %} 113 | {% render_built_form slug=form_instance.slug %} 114 | 115 | This allows forms to be displayed without having a form instance, using 116 | a form's slug or ID, which could be hard-coded in a template, or stored 117 | in another model instance. 118 | 119 | 120 | File Uploads 121 | ============ 122 | 123 | It's possible for admin users to create forms that allow file uploads 124 | which can be accessed via a download URL for each file that is provided 125 | in the CSV export. By default these uploaded files are stored in an 126 | obscured location under your project's ``MEDIA_ROOT`` directory but 127 | ideally the should be stored somewhere inaccessible to the public. To 128 | set the location where files are stored to be somewhere outside of your 129 | project's ``MEDIA_ROOT`` directory you just need to define the 130 | ``FORMS_BUILDER_UPLOAD_ROOT`` setting in your project's ``settings`` 131 | module. Its value should be an absolute path on the web server that 132 | isn't accessible to the public. 133 | 134 | 135 | Configuration 136 | ============= 137 | 138 | The following settings can be defined in your project's ``settings`` 139 | module. 140 | 141 | * ``FORMS_BUILDER_FIELD_MAX_LENGTH`` - Maximum allowed length for 142 | field values. Defaults to ``2000`` 143 | * ``FORMS_BUILDER_LABEL_MAX_LENGTH`` - Maximum allowed length for 144 | field labels. Defaults to ``20`` 145 | * ``FORMS_BUILDER_EXTRA_FIELDS`` - Sequence of custom fields that 146 | will be added to the form field types. Defaults to ``()`` 147 | * ``FORMS_BUILDER_UPLOAD_ROOT`` - The absolute path where files will 148 | be uploaded to. Defaults to ``None`` 149 | * ``FORMS_BUILDER_USE_HTML5`` - Boolean controlling whether HTML5 form 150 | fields are used. Defaults to ``True`` 151 | * ``FORMS_BUILDER_USE_SITES`` - Boolean controlling whether forms are 152 | associated to Django's Sites framework. 153 | Defaults to ``"django.contrib.sites" in settings.INSTALLED_APPS`` 154 | * ``FORMS_BUILDER_EDITABLE_SLUGS`` - Boolean controlling whether form 155 | slugs are editable in the admin. Defaults to ``False`` 156 | * ``FORMS_BUILDER_CHOICES_QUOTE`` - Char to start a quoted choice with. 157 | Defaults to the backtick char: ` 158 | * ``FORMS_BUILDER_CHOICES_UNQUOTE`` - Char to end a quoted choice with. 159 | Defaults to the backtick char: ` 160 | * ``FORMS_BUILDER_CSV_DELIMITER`` - Char to use as a field delimiter 161 | when exporting form responses as CSV. Defaults to a comma: , 162 | * ``FORMS_BUILDER_EMAIL_FAIL_SILENTLY`` - Bool used for Django's 163 | ``fail_silently`` argument when sending email. 164 | Defaults to ``settings.DEBUG``. 165 | 166 | 167 | Custom Fields and Widgets 168 | ========================= 169 | 170 | You can also add your own custom fields or widgets to the choices of 171 | fields available for a form. Simply define a sequence for the 172 | ``FORMS_BUILDER_EXTRA_FIELDS`` setting in your project's ``settings`` 173 | module, where each item in the sequence is a custom field that will 174 | be available. 175 | 176 | Each field in the sequence should be a three-item sequence containing 177 | an ID, a dotted import path for the field class, and a field name, for 178 | each custom field type. The ID is simply a numeric constant for the 179 | field, but cannot be a value already used, so choose a high number 180 | such as 100 or greater to avoid conflicts:: 181 | 182 | FORMS_BUILDER_EXTRA_FIELDS = ( 183 | (100, "django.forms.BooleanField", "My cool checkbox"), 184 | (101, "my_module.MyCustomField", "Another field"), 185 | ) 186 | 187 | You can also define custom widget classes for any of the existing or 188 | custom form fields via the ``FORMS_BUILDER_EXTRA_WIDGETS`` setting. 189 | Each field in the sequence should be a two-item sequence containing 190 | the same ID referred to above for the form field class, and a dotted 191 | import path for the widget class:: 192 | 193 | FORMS_BUILDER_EXTRA_WIDGETS = ( 194 | (100, "my_module.MyCoolWidget"), 195 | (101, "my_other_module.AnotherWidget"), 196 | ) 197 | 198 | Note that using the ``FORMS_BUILDER_EXTRA_WIDGETS`` setting to define 199 | custom widgets for field classes of your own is somewhat redundant, 200 | since you could simply define the widgets on the field classes directly 201 | in their code. 202 | 203 | 204 | Email Templates 205 | =============== 206 | 207 | The `django-email-extras`_ package is used to send multipart email 208 | notifications using Django's templating system for constructing the 209 | emails, to users submitting forms, and any recipients specified when 210 | creating a form via Django's admin. 211 | 212 | Templates for HTML and text versions of the email can be found in the 213 | ``templates/email_extras`` directory. This allows you to customize the 214 | look and feel of emails that are sent to form submitters. Along with 215 | each of the ``form_response`` email templates which are used to email 216 | the form submitter, you'll also find corresponding 217 | ``form_response_copies`` templates, that extend the former set - these 218 | are used as the templates for emailing any extra recipients specified 219 | for the form in the admin interface. By default they simply extend 220 | the ``form_response`` templates, but you can modify them should you 221 | need to customize the emails sent to any extra recipients. 222 | 223 | .. note:: 224 | 225 | With ``django-email-extras`` installed, it's also possible to 226 | configure `PGP`_ encrypted emails to be send to staff members, 227 | allowing forms to be built for capturing sensitive information. 228 | Consult the `django-email-extras`_ documentation for more info. 229 | 230 | 231 | Signals 232 | ======= 233 | 234 | Two signals are provided for hooking into different states of the form 235 | submission process. 236 | 237 | * ``form_invalid(sender=request, form=form)`` - Sent when the form is 238 | submitted with invalid data. 239 | * ``form_valid(sender=request, form=form, entry=entry)`` - Sent when 240 | the form is submitted with valid data. 241 | 242 | For each signal the sender argument is the current request. Both 243 | signals receive a ``form`` argument is given which is the 244 | ``FormForForm`` instance, a ``ModelForm`` for the ``FormEntry`` model. 245 | The ``form_valid`` signal also receives a ``entry`` argument, which is 246 | the ``FormEntry`` model instance created. 247 | 248 | Some examples of using the signals would be to monitor how users are 249 | causing validation errors with the form, or a pipeline of events to 250 | occur on successful form submissions. Suppose we wanted to store a 251 | logged in user's username against each form when submitted, given 252 | a form containing a field with the label ``Username`` with its 253 | field_type set to ``Hidden``:: 254 | 255 | from django.dispatch import receiver 256 | from forms_builder.forms.signals import form_valid 257 | 258 | @receiver(form_valid) 259 | def set_username(sender=None, form=None, entry=None, **kwargs): 260 | request = sender 261 | if request.user.is_authenticated(): 262 | field = entry.form.fields.get(label="Username") 263 | field_entry, _ = entry.fields.get_or_create(field_id=field.id) 264 | field_entry.value = request.user.username 265 | field_entry.save() 266 | 267 | 268 | Dynamic Field Defaults 269 | ====================== 270 | 271 | As of version 0.6, you can use Django template code for default field 272 | values. For example you could enter ``{{ request.user.username }}`` and 273 | the field will be pre-populated with a user's username if they're 274 | authenticated. 275 | 276 | 277 | XLS Export 278 | ========== 279 | 280 | By default, django-forms-builder provides export of form entries via 281 | CSV file. You can also enable export via XLS file (Microsoft Excel) 282 | by installing the `xlwt`_ package:: 283 | 284 | $ pip install xlwt 285 | 286 | 287 | .. _`pip`: http://www.pip-installer.org/ 288 | .. _`South`: http://south.aeracode.org/ 289 | .. _`django-email-extras`: https://github.com/stephenmcd/django-email-extras 290 | .. _`PGP`: http://en.wikipedia.org/wiki/Pretty_Good_Privacy 291 | .. _`xlwt`: http://www.python-excel.org/ 292 | -------------------------------------------------------------------------------- /forms_builder/forms/forms.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from future.builtins import int, range, str 3 | 4 | from datetime import date, datetime 5 | from os.path import join, split 6 | from uuid import uuid4 7 | 8 | import django 9 | from django import forms 10 | from django.forms.extras import SelectDateWidget 11 | from django.core.files.storage import default_storage 12 | from django.core.urlresolvers import reverse 13 | from django.template import Template 14 | from django.utils.safestring import mark_safe 15 | from django.utils.translation import ugettext_lazy as _ 16 | 17 | from forms_builder.forms import fields 18 | from forms_builder.forms.models import FormEntry, FieldEntry 19 | from forms_builder.forms import settings 20 | from forms_builder.forms.utils import now, split_choices 21 | 22 | 23 | fs = default_storage 24 | if settings.UPLOAD_ROOT is not None: 25 | fs = default_storage.__class__(location=settings.UPLOAD_ROOT) 26 | 27 | 28 | ############################## 29 | # Each type of export filter # 30 | ############################## 31 | 32 | # Text matches 33 | FILTER_CHOICE_CONTAINS = "1" 34 | FILTER_CHOICE_DOESNT_CONTAIN = "2" 35 | 36 | # Exact matches 37 | FILTER_CHOICE_EQUALS = "3" 38 | FILTER_CHOICE_DOESNT_EQUAL = "4" 39 | 40 | # Greater/less than 41 | FILTER_CHOICE_BETWEEN = "5" 42 | 43 | # Multiple values 44 | FILTER_CHOICE_CONTAINS_ANY = "6" 45 | FILTER_CHOICE_CONTAINS_ALL = "7" 46 | FILTER_CHOICE_DOESNT_CONTAIN_ANY = "8" 47 | FILTER_CHOICE_DOESNT_CONTAIN_ALL = "9" 48 | 49 | ########################## 50 | # Export filters grouped # 51 | ########################## 52 | 53 | # Text fields 54 | TEXT_FILTER_CHOICES = ( 55 | ("", _("Nothing")), 56 | (FILTER_CHOICE_CONTAINS, _("Contains")), 57 | (FILTER_CHOICE_DOESNT_CONTAIN, _("Doesn't contain")), 58 | (FILTER_CHOICE_EQUALS, _("Equals")), 59 | (FILTER_CHOICE_DOESNT_EQUAL, _("Doesn't equal")), 60 | ) 61 | 62 | # Choices with single value entries 63 | CHOICE_FILTER_CHOICES = ( 64 | ("", _("Nothing")), 65 | (FILTER_CHOICE_CONTAINS_ANY, _("Equals any")), 66 | (FILTER_CHOICE_DOESNT_CONTAIN_ANY, _("Doesn't equal any")), 67 | ) 68 | 69 | # Choices with multiple value entries 70 | MULTIPLE_FILTER_CHOICES = ( 71 | ("", _("Nothing")), 72 | (FILTER_CHOICE_CONTAINS_ANY, _("Contains any")), 73 | (FILTER_CHOICE_CONTAINS_ALL, _("Contains all")), 74 | (FILTER_CHOICE_DOESNT_CONTAIN_ANY, _("Doesn't contain any")), 75 | (FILTER_CHOICE_DOESNT_CONTAIN_ALL, _("Doesn't contain all")), 76 | ) 77 | 78 | # Dates 79 | DATE_FILTER_CHOICES = ( 80 | ("", _("Nothing")), 81 | (FILTER_CHOICE_BETWEEN, _("Is between")), 82 | ) 83 | 84 | # The filter function for each filter type 85 | FILTER_FUNCS = { 86 | FILTER_CHOICE_CONTAINS: 87 | lambda val, field: val.lower() in field.lower(), 88 | FILTER_CHOICE_DOESNT_CONTAIN: 89 | lambda val, field: val.lower() not in field.lower(), 90 | FILTER_CHOICE_EQUALS: 91 | lambda val, field: val.lower() == field.lower(), 92 | FILTER_CHOICE_DOESNT_EQUAL: 93 | lambda val, field: val.lower() != field.lower(), 94 | FILTER_CHOICE_BETWEEN: 95 | lambda val_from, val_to, field: ( 96 | (not val_from or val_from <= field) and 97 | (not val_to or val_to >= field) 98 | ), 99 | FILTER_CHOICE_CONTAINS_ANY: 100 | lambda val, field: set(val) & set(split_choices(field)), 101 | FILTER_CHOICE_CONTAINS_ALL: 102 | lambda val, field: set(val) == set(split_choices(field)), 103 | FILTER_CHOICE_DOESNT_CONTAIN_ANY: 104 | lambda val, field: not set(val) & set(split_choices(field)), 105 | FILTER_CHOICE_DOESNT_CONTAIN_ALL: 106 | lambda val, field: set(val) != set(split_choices(field)), 107 | } 108 | 109 | # Export form fields for each filter type grouping 110 | text_filter_field = forms.ChoiceField(label=" ", required=False, 111 | choices=TEXT_FILTER_CHOICES) 112 | choice_filter_field = forms.ChoiceField(label=" ", required=False, 113 | choices=CHOICE_FILTER_CHOICES) 114 | multiple_filter_field = forms.ChoiceField(label=" ", required=False, 115 | choices=MULTIPLE_FILTER_CHOICES) 116 | date_filter_field = forms.ChoiceField(label=" ", required=False, 117 | choices=DATE_FILTER_CHOICES) 118 | 119 | 120 | class FormForForm(forms.ModelForm): 121 | field_entry_model = FieldEntry 122 | 123 | class Meta: 124 | model = FormEntry 125 | exclude = ("form", "entry_time") 126 | 127 | def __init__(self, form, context, *args, **kwargs): 128 | """ 129 | Dynamically add each of the form fields for the given form model 130 | instance and its related field model instances. 131 | """ 132 | self.form = form 133 | self.form_fields = form.fields.visible() 134 | initial = kwargs.pop("initial", {}) 135 | # If a FormEntry instance is given to edit, stores it's field 136 | # values for using as initial data. 137 | field_entries = {} 138 | if kwargs.get("instance"): 139 | for field_entry in kwargs["instance"].fields.all(): 140 | field_entries[field_entry.field_id] = field_entry.value 141 | super(FormForForm, self).__init__(*args, **kwargs) 142 | # Create the form fields. 143 | for field in self.form_fields: 144 | field_key = field.slug 145 | field_class = fields.CLASSES[field.field_type] 146 | field_widget = fields.WIDGETS.get(field.field_type) 147 | field_args = {"label": field.label, "required": field.required, 148 | "help_text": field.help_text} 149 | arg_names = field_class.__init__.__code__.co_varnames 150 | if "max_length" in arg_names: 151 | field_args["max_length"] = settings.FIELD_MAX_LENGTH 152 | if "choices" in arg_names: 153 | choices = list(field.get_choices()) 154 | if (field.field_type == fields.SELECT and 155 | field.default not in [c[0] for c in choices]): 156 | choices.insert(0, ("", field.placeholder_text)) 157 | field_args["choices"] = choices 158 | if field_widget is not None: 159 | field_args["widget"] = field_widget 160 | # 161 | # Initial value for field, in order of preference: 162 | # 163 | # - If a form model instance is given (eg we're editing a 164 | # form response), then use the instance's value for the 165 | # field. 166 | # - If the developer has provided an explicit "initial" 167 | # dict, use it. 168 | # - The default value for the field instance as given in 169 | # the admin. 170 | # 171 | initial_val = None 172 | try: 173 | initial_val = field_entries[field.id] 174 | except KeyError: 175 | try: 176 | initial_val = initial[field_key] 177 | except KeyError: 178 | initial_val = Template(field.default).render(context) 179 | if initial_val: 180 | if field.is_a(*fields.MULTIPLE): 181 | initial_val = split_choices(initial_val) 182 | if field.field_type == fields.CHECKBOX: 183 | initial_val = initial_val != "False" 184 | self.initial[field_key] = initial_val 185 | self.fields[field_key] = field_class(**field_args) 186 | 187 | if field.field_type == fields.DOB: 188 | now = datetime.now() 189 | years = list(range(now.year, now.year - 120, -1)) 190 | self.fields[field_key].widget.years = years 191 | 192 | # Add identifying CSS classes to the field. 193 | css_class = field_class.__name__.lower() 194 | if field.required: 195 | css_class += " required" 196 | if (settings.USE_HTML5 and 197 | field.field_type != fields.CHECKBOX_MULTIPLE): 198 | self.fields[field_key].widget.attrs["required"] = "" 199 | self.fields[field_key].widget.attrs["class"] = css_class 200 | if field.placeholder_text and not field.default: 201 | text = field.placeholder_text 202 | self.fields[field_key].widget.attrs["placeholder"] = text 203 | 204 | def save(self, **kwargs): 205 | """ 206 | Get/create a FormEntry instance and assign submitted values to 207 | related FieldEntry instances for each form field. 208 | """ 209 | entry = super(FormForForm, self).save(commit=False) 210 | entry.form = self.form 211 | entry.entry_time = now() 212 | entry.save() 213 | entry_fields = entry.fields.values_list("field_id", flat=True) 214 | new_entry_fields = [] 215 | for field in self.form_fields: 216 | field_key = field.slug 217 | value = self.cleaned_data[field_key] 218 | if value and self.fields[field_key].widget.needs_multipart_form: 219 | value = fs.save(join("forms", str(uuid4()), value.name), value) 220 | if isinstance(value, list): 221 | value = ", ".join([v.strip() for v in value]) 222 | if field.id in entry_fields: 223 | field_entry = entry.fields.get(field_id=field.id) 224 | field_entry.value = value 225 | field_entry.save() 226 | else: 227 | new = {"entry": entry, "field_id": field.id, "value": value} 228 | new_entry_fields.append(self.field_entry_model(**new)) 229 | if new_entry_fields: 230 | if django.VERSION >= (1, 4, 0): 231 | self.field_entry_model.objects.bulk_create(new_entry_fields) 232 | else: 233 | for field_entry in new_entry_fields: 234 | field_entry.save() 235 | return entry 236 | 237 | def email_to(self): 238 | """ 239 | Return the value entered for the first field of type EmailField. 240 | """ 241 | for field in self.form_fields: 242 | if field.is_a(fields.EMAIL): 243 | return self.cleaned_data[field.slug] 244 | return None 245 | 246 | 247 | class EntriesForm(forms.Form): 248 | """ 249 | Form with a set of fields dynamically assigned that can be used to 250 | filter entries for the given ``forms.models.Form`` instance. 251 | """ 252 | 253 | def __init__(self, form, request, formentry_model=FormEntry, 254 | fieldentry_model=FieldEntry, *args, **kwargs): 255 | """ 256 | Iterate through the fields of the ``forms.models.Form`` instance and 257 | create the form fields required to control including the field in 258 | the export (with a checkbox) or filtering the field which differs 259 | across field types. User a list of checkboxes when a fixed set of 260 | choices can be chosen from, a pair of date fields for date ranges, 261 | and for all other types provide a textbox for text search. 262 | """ 263 | self.form = form 264 | self.request = request 265 | self.formentry_model = formentry_model 266 | self.fieldentry_model = fieldentry_model 267 | self.form_fields = form.fields.all() 268 | self.entry_time_name = str(self.formentry_model._meta.get_field( 269 | "entry_time").verbose_name) 270 | super(EntriesForm, self).__init__(*args, **kwargs) 271 | for field in self.form_fields: 272 | field_key = "field_%s" % field.id 273 | # Checkbox for including in export. 274 | self.fields["%s_export" % field_key] = forms.BooleanField( 275 | label=field.label, initial=True, required=False) 276 | if field.is_a(*fields.CHOICES): 277 | # A fixed set of choices to filter by. 278 | if field.is_a(fields.CHECKBOX): 279 | choices = ((True, _("Checked")), (False, _("Not checked"))) 280 | else: 281 | choices = field.get_choices() 282 | contains_field = forms.MultipleChoiceField(label=" ", 283 | choices=choices, widget=forms.CheckboxSelectMultiple(), 284 | required=False) 285 | self.fields["%s_filter" % field_key] = choice_filter_field 286 | self.fields["%s_contains" % field_key] = contains_field 287 | elif field.is_a(*fields.MULTIPLE): 288 | # A fixed set of choices to filter by, with multiple 289 | # possible values in the entry field. 290 | contains_field = forms.MultipleChoiceField(label=" ", 291 | choices=field.get_choices(), 292 | widget=forms.CheckboxSelectMultiple(), 293 | required=False) 294 | self.fields["%s_filter" % field_key] = multiple_filter_field 295 | self.fields["%s_contains" % field_key] = contains_field 296 | elif field.is_a(*fields.DATES): 297 | # A date range to filter by. 298 | self.fields["%s_filter" % field_key] = date_filter_field 299 | self.fields["%s_from" % field_key] = forms.DateField( 300 | label=" ", widget=SelectDateWidget(), required=False) 301 | self.fields["%s_to" % field_key] = forms.DateField( 302 | label=_("and"), widget=SelectDateWidget(), required=False) 303 | else: 304 | # Text box for search term to filter by. 305 | contains_field = forms.CharField(label=" ", required=False) 306 | self.fields["%s_filter" % field_key] = text_filter_field 307 | self.fields["%s_contains" % field_key] = contains_field 308 | # Add ``FormEntry.entry_time`` as a field. 309 | field_key = "field_0" 310 | label = self.formentry_model._meta.get_field("entry_time").verbose_name 311 | self.fields["%s_export" % field_key] = forms.BooleanField( 312 | initial=True, label=label, required=False) 313 | self.fields["%s_filter" % field_key] = date_filter_field 314 | self.fields["%s_from" % field_key] = forms.DateField( 315 | label=" ", widget=SelectDateWidget(), required=False) 316 | self.fields["%s_to" % field_key] = forms.DateField( 317 | label=_("and"), widget=SelectDateWidget(), required=False) 318 | 319 | def __iter__(self): 320 | """ 321 | Yield pairs of include checkbox / filters for each field. 322 | """ 323 | for field_id in [f.id for f in self.form_fields] + [0]: 324 | prefix = "field_%s_" % field_id 325 | fields = [f for f in super(EntriesForm, self).__iter__() 326 | if f.name.startswith(prefix)] 327 | yield fields[0], fields[1], fields[2:] 328 | 329 | def posted_data(self, field): 330 | """ 331 | Wrapper for self.cleaned_data that returns True on 332 | field_id_export fields when the form hasn't been posted to, 333 | to facilitate show/export URLs that export all entries without 334 | a form submission. 335 | """ 336 | try: 337 | return self.cleaned_data[field] 338 | except (AttributeError, KeyError): 339 | return field.endswith("_export") 340 | 341 | def columns(self): 342 | """ 343 | Returns the list of selected column names. 344 | """ 345 | fields = [f.label for f in self.form_fields 346 | if self.posted_data("field_%s_export" % f.id)] 347 | if self.posted_data("field_0_export"): 348 | fields.append(self.entry_time_name) 349 | return fields 350 | 351 | def rows(self, csv=False): 352 | """ 353 | Returns each row based on the selected criteria. 354 | """ 355 | 356 | # Store the index of each field against its ID for building each 357 | # entry row with columns in the correct order. Also store the IDs of 358 | # fields with a type of FileField or Date-like for special handling of 359 | # their values. 360 | field_indexes = {} 361 | file_field_ids = [] 362 | date_field_ids = [] 363 | for field in self.form_fields: 364 | if self.posted_data("field_%s_export" % field.id): 365 | field_indexes[field.id] = len(field_indexes) 366 | if field.is_a(fields.FILE): 367 | file_field_ids.append(field.id) 368 | elif field.is_a(*fields.DATES): 369 | date_field_ids.append(field.id) 370 | num_columns = len(field_indexes) 371 | include_entry_time = self.posted_data("field_0_export") 372 | if include_entry_time: 373 | num_columns += 1 374 | 375 | # Get the field entries for the given form and filter by entry_time 376 | # if specified. 377 | model = self.fieldentry_model 378 | field_entries = model.objects.filter(entry__form=self.form 379 | ).order_by("-entry__id").select_related("entry") 380 | if self.posted_data("field_0_filter") == FILTER_CHOICE_BETWEEN: 381 | time_from = self.posted_data("field_0_from") 382 | time_to = self.posted_data("field_0_to") 383 | if time_from and time_to: 384 | field_entries = field_entries.filter( 385 | entry__entry_time__range=(time_from, time_to)) 386 | 387 | # Loop through each field value ordered by entry, building up each 388 | # entry as a row. Use the ``valid_row`` flag for marking a row as 389 | # invalid if it fails one of the filtering criteria specified. 390 | current_entry = None 391 | current_row = None 392 | valid_row = True 393 | for field_entry in field_entries: 394 | if field_entry.entry_id != current_entry: 395 | # New entry, write out the current row and start a new one. 396 | if valid_row and current_row is not None: 397 | if not csv: 398 | current_row.insert(0, current_entry) 399 | yield current_row 400 | current_entry = field_entry.entry_id 401 | current_row = [""] * num_columns 402 | valid_row = True 403 | if include_entry_time: 404 | current_row[-1] = field_entry.entry.entry_time 405 | field_value = field_entry.value or "" 406 | # Check for filter. 407 | field_id = field_entry.field_id 408 | filter_type = self.posted_data("field_%s_filter" % field_id) 409 | filter_args = None 410 | if filter_type: 411 | if filter_type == FILTER_CHOICE_BETWEEN: 412 | f, t = "field_%s_from" % field_id, "field_%s_to" % field_id 413 | filter_args = [self.posted_data(f), self.posted_data(t)] 414 | else: 415 | field_name = "field_%s_contains" % field_id 416 | filter_args = self.posted_data(field_name) 417 | if filter_args: 418 | filter_args = [filter_args] 419 | if filter_args: 420 | # Convert dates before checking filter. 421 | if field_id in date_field_ids: 422 | try: 423 | y, m, d = field_value.split(" ")[0].split("-") 424 | except ValueError: 425 | filter_args.append(field_value) 426 | else: 427 | dte = date(int(y), int(m), int(d)) 428 | filter_args.append(dte) 429 | else: 430 | filter_args.append(field_value) 431 | filter_func = FILTER_FUNCS[filter_type] 432 | if not filter_func(*filter_args): 433 | valid_row = False 434 | # Create download URL for file fields. 435 | if field_entry.value and field_id in file_field_ids: 436 | url = reverse("admin:form_file", args=(field_entry.id,)) 437 | field_value = self.request.build_absolute_uri(url) 438 | if not csv: 439 | parts = (field_value, split(field_entry.value)[1]) 440 | field_value = mark_safe("%s" % parts) 441 | # Only use values for fields that were selected. 442 | try: 443 | current_row[field_indexes[field_id]] = field_value 444 | except KeyError: 445 | pass 446 | # Output the final row. 447 | if valid_row and current_row is not None: 448 | if not csv: 449 | current_row.insert(0, current_entry) 450 | yield current_row 451 | --------------------------------------------------------------------------------