├── .coveragerc ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── AUTHORS ├── LICENSE ├── MANIFEST.in ├── README.rst ├── dfd_tests ├── __init__.py ├── settings.py ├── urls.py └── wsgi.py ├── form_designer ├── __init__.py ├── admin.py ├── apps.py ├── contrib │ ├── __init__.py │ └── exporters │ │ ├── __init__.py │ │ ├── csv_exporter.py │ │ └── xls_exporter.py ├── email.py ├── fields.py ├── forms.py ├── locale │ ├── de │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── en │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── es │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── fi │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── fr │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── nl │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── pt_BR │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── ru │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ └── sv │ │ └── LC_MESSAGES │ │ ├── django.mo │ │ └── django.po ├── migrations │ ├── 0001_initial.py │ ├── 0002_reply_to.py │ ├── 0003_mail_cover_text.py │ └── __init__.py ├── models.py ├── settings.py ├── signals.py ├── static │ └── form_designer │ │ └── js │ │ ├── jquery-inline-collapsible.js │ │ ├── jquery-inline-fieldset-collapsible.js │ │ ├── jquery-inline-positioning.js │ │ ├── jquery-inline-prepopulate-label.js │ │ ├── jquery-inline-rename.js │ │ ├── jquery-ui.js │ │ ├── jquery-url-param.js │ │ └── jquery.js ├── templates │ ├── admin │ │ └── form_designer │ │ │ └── formlog │ │ │ └── change_list.html │ ├── html │ │ └── formdefinition │ │ │ ├── base.html │ │ │ ├── data_message.html │ │ │ ├── data_table_message.html │ │ │ ├── detail.html │ │ │ └── forms │ │ │ ├── as_p.html │ │ │ ├── as_table.html │ │ │ ├── as_table_h.html │ │ │ ├── as_ul.html │ │ │ ├── custom.html │ │ │ └── includes │ │ │ └── submit.html │ └── txt │ │ └── formdefinition │ │ └── data_message.txt ├── templatetags │ ├── __init__.py │ ├── friendly.py │ └── widget_type.py ├── tests │ ├── __init__.py │ ├── conftest.py │ ├── test_admin.py │ ├── test_basics.py │ └── test_messages.py ├── uploads.py ├── urls.py ├── utils.py └── views.py ├── pyproject.toml └── tox.ini /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | branch = True 3 | omit = 4 | *migrations* 5 | *management* 6 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | "on": 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | branches: 8 | - master 9 | jobs: 10 | Build: 11 | runs-on: ubuntu-24.04 12 | strategy: 13 | matrix: 14 | python-version: ["3.8", "3.10", "3.12", "3.13"] 15 | DATABASE: ["mysql", "sqlite"] 16 | steps: 17 | - name: "Set up Python ${{ matrix.python-version }}" 18 | uses: actions/setup-python@v2 19 | with: 20 | python-version: "${{ matrix.python-version }}" 21 | - uses: actions/checkout@v2 22 | - uses: actions/cache@v4 23 | with: 24 | path: ~/.cache/pip 25 | key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} 26 | restore-keys: | 27 | ${{ runner.os }}-pip- 28 | - run: pip install -U pip 29 | - run: pip install 'tox>=4.6.0' tox-gh-actions codecov 30 | - run: tox -v 31 | env: 32 | DATABASE_USER: root 33 | DATABASE_PASSWORD: root 34 | DJANGO_SETTINGS_MODULE: dfd_tests.settings 35 | - run: codecov -e TRAVIS_PYTHON_VERSION -e DATABASE 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *$py.class 2 | *,cover 3 | *.egg 4 | *.egg-info/ 5 | *.log 6 | *.manifest 7 | *.mo 8 | *.pot 9 | *.py[cod] 10 | *.pyc 11 | *.so 12 | *.spec 13 | *.sqlite3 14 | .cache 15 | .coverage 16 | .coverage.* 17 | .eggs/ 18 | .env 19 | .hypothesis/ 20 | .idea 21 | .installed.cfg 22 | .ipynb_checkpoints 23 | .pytest_cache 24 | .Python 25 | .python-version 26 | .ropeproject 27 | .scrapy 28 | .spyderproject 29 | .tox/ 30 | .webassets-cache 31 | __pycache__/ 32 | build/ 33 | celerybeat-schedule 34 | coverage.xml 35 | develop-eggs/ 36 | dist 37 | dist/ 38 | docs/_build/ 39 | downloads/ 40 | eggs/ 41 | env/ 42 | ENV/ 43 | htmlcov/ 44 | instance/ 45 | lib/ 46 | lib64/ 47 | local_settings.py 48 | MANIFEST 49 | nosetests.xml 50 | parts/ 51 | pip-delete-this-directory.txt 52 | pip-log.txt 53 | sdist/ 54 | target/ 55 | var/ 56 | venv/ 57 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | AUTHORS AND MAINTAINERS 2 | 3 | ORIGINAL VERSION DEVELOPED BY: 4 | Samuel Luescher (samluescher) 5 | 6 | CONTRIBUTORS (in chronological order): 7 | Jannis Leidel (jezdez) 8 | Simon Charette (charettes) 9 | William Dangerfield (dangerfield) 10 | kinea 11 | John-Scott Atlakson (jsma) 12 | Diederik van der Boor (vdboor) 13 | Jeremy Self (finder) 14 | Noemi Millman (sbnoemi) 15 | Patrick Taylor (huxley) 16 | keysen 17 | ruhan 18 | Dries Desmet (driesdesmet) 19 | DeeJayPee 20 | 7mp 21 | Evan Borgstrom (borgstrom) 22 | Ceesjan Luiten (quinox) 23 | Jordi Llonch (llonchj) 24 | Aarni Koskela (akx) 25 | Abdourakhmane Ripault (aert) 26 | Estuans (estuans) 27 | bloynd 28 | Tai Lee (mrmachine) 29 | Ben Waters (the-ben-waters) 30 | Yves Serrano (yvess) 31 | Fabian Frei (faebser) 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009-2014, Samuel Luescher 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above 11 | copyright notice, this list of conditions and the following 12 | disclaimer in the documentation and/or other materials provided 13 | with the distribution. 14 | * Neither the name of the author nor the names of other 15 | contributors may be used to endorse or promote products derived 16 | from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include AUTHORS 2 | include README.rst 3 | include LICENSE 4 | recursive-include form_designer/templates *.html *.txt 5 | recursive-include form_designer/static *.js 6 | recursive-include form_designer/locale *.po *.mo 7 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Django Form Designer (AI Fork) 2 | ****************************** 3 | 4 | Unmaintained 5 | ============ 6 | 7 | This fork is unmaintained. There's an alternative fork at 8 | https://github.com/kcsry/django-form-designer 9 | 10 | Acknowledgements 11 | ================ 12 | 13 | This project is a fork of https://github.com/samluescher/django-form-designer . 14 | Thanks, @samluescher! 15 | 16 | This fork is compatible with Django 4+ and Python 3.7+. 17 | 18 | General 19 | ======= 20 | 21 | A Django admin app with a GUI to create complex forms without any programming skills; 22 | complete with logging, validation, and redirects. 23 | 24 | **Key features**: 25 | 26 | * Design contact forms, search forms etc from the Django admin, without writing any code 27 | * Form data can be logged and CSV-exported, sent via e-mail, or forwarded to any web address 28 | * Use drag & drop to change the position of your form fields 29 | * Fully collapsible admin interface for better overview over your form 30 | * Implements many form fields included with Django (TextField, EmailField, DateField etc) 31 | * Validation rules as supplied by Django are fully configurable (maximum length, regular 32 | expression etc) 33 | * Customizable messages and labels 34 | * Supports POST and GET forms 35 | * Signals on form render, submission, success, error. 36 | * Supports google reCAPTCHA service 37 | 38 | 39 | Basic setup 40 | =========== 41 | 42 | - Add ``form_designer`` to your ``INSTALLED_APPS`` setting:: 43 | 44 | INSTALLED_APPS = ( 45 | ... 46 | 'form_designer', 47 | ) 48 | 49 | - For basic usage, add URLs to your URL conf. For instance, in order to make a form named 50 | ``example-form`` available under ``http://domain.com/forms/example-form``, 51 | add the following line to your project's ``urls.py``:: 52 | 53 | urlpatterns = patterns('', 54 | (r'^forms/', include('form_designer.urls')), 55 | ... 56 | ) 57 | 58 | 59 | Optional requirements 60 | ===================== 61 | 62 | The form_designer admin interface requires jQuery and the jQuery UI Sortable 63 | plugin to make building forms a lot more user-friendly. The two Javascript 64 | files are bundled with form_designer. If you want to use you own jquery.js 65 | instead because you're already including it anyway, define JQUERY\_JS in your 66 | settings file. For instance:: 67 | 68 | JQUERY_JS = 'jquery/jquery-latest.js' 69 | 70 | Running tests 71 | ============= 72 | 73 | Use `tox`. 74 | -------------------------------------------------------------------------------- /dfd_tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andersinno/django-form-designer-ai/104f205035681e0743372f6f22de96bc6eca9d6c/dfd_tests/__init__.py -------------------------------------------------------------------------------- /dfd_tests/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | from tempfile import gettempdir 3 | 4 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 5 | SECRET_KEY = "&nsa)3w(oz6^a1e-dj+iw9=jqps6az(&l2khgqtr)%%sj8ky@(" 6 | DEBUG = True 7 | ALLOWED_HOSTS = [] 8 | 9 | LANGUAGE_CODE = "en" 10 | LANGUAGES = [("en", "en")] 11 | TIME_ZONE = "UTC" 12 | USE_I18N = True 13 | USE_L10N = True 14 | USE_TZ = True 15 | STATIC_URL = "/static/" 16 | MEDIA_ROOT = gettempdir() 17 | WSGI_APPLICATION = "dfd_tests.wsgi.application" 18 | ROOT_URLCONF = "dfd_tests.urls" 19 | SITE_ID = 1 20 | 21 | INSTALLED_APPS = [ 22 | "django.contrib.admin", 23 | "django.contrib.auth", 24 | "django.contrib.contenttypes", 25 | "django.contrib.sessions", 26 | "django.contrib.messages", 27 | "django.contrib.staticfiles", 28 | "django.contrib.sites", 29 | "dfd_tests", 30 | "form_designer", 31 | ] 32 | 33 | MIDDLEWARE = [ 34 | "django.middleware.security.SecurityMiddleware", 35 | "django.contrib.sessions.middleware.SessionMiddleware", 36 | "django.middleware.common.CommonMiddleware", 37 | "django.middleware.csrf.CsrfViewMiddleware", 38 | "django.contrib.auth.middleware.AuthenticationMiddleware", 39 | "django.contrib.messages.middleware.MessageMiddleware", 40 | "django.middleware.clickjacking.XFrameOptionsMiddleware", 41 | ] 42 | 43 | TEMPLATES = [ 44 | { 45 | "BACKEND": "django.template.backends.django.DjangoTemplates", 46 | "DIRS": [], 47 | "APP_DIRS": True, 48 | "OPTIONS": { 49 | "context_processors": [ 50 | "django.template.context_processors.debug", 51 | "django.template.context_processors.request", 52 | "django.contrib.auth.context_processors.auth", 53 | "django.contrib.messages.context_processors.messages", 54 | ], 55 | }, 56 | }, 57 | ] 58 | 59 | DATABASES = { 60 | "default": { 61 | "ENGINE": "django.db.backends.sqlite3", 62 | "NAME": os.path.join(BASE_DIR, "db.sqlite3"), 63 | } 64 | } 65 | 66 | if os.environ.get("DATABASE") == "mysql": 67 | DATABASES = { 68 | "default": { 69 | "ENGINE": "django.db.backends.mysql", 70 | "NAME": "dfd_tests", 71 | "USER": os.environ["DATABASE_USER"], 72 | "PASSWORD": os.environ.get("DATABASE_PASSWORD", ""), 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /dfd_tests/urls.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.urls import path 3 | 4 | urlpatterns = [ 5 | path("admin/", admin.site.urls), 6 | ] 7 | -------------------------------------------------------------------------------- /dfd_tests/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for dfd_tests project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dfd_tests.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /form_designer/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = "2.1.1.1" 2 | -------------------------------------------------------------------------------- /form_designer/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.http import Http404 3 | from django.urls import re_path, reverse 4 | from django.utils.module_loading import import_string 5 | from django.utils.translation import gettext_lazy as _ 6 | 7 | from form_designer import settings 8 | from form_designer.forms import FormDefinitionFieldInlineForm, FormDefinitionForm 9 | from form_designer.models import FormDefinition, FormDefinitionField, FormLog 10 | 11 | 12 | class FormDefinitionFieldInline(admin.StackedInline): 13 | form = FormDefinitionFieldInlineForm 14 | model = FormDefinitionField 15 | extra = 1 16 | fieldsets = [ 17 | (_("Basic"), {"fields": ["name", "field_class", "required", "initial"]}), 18 | ( 19 | _("Display"), 20 | {"fields": ["label", "widget", "help_text", "position", "include_result"]}, 21 | ), 22 | (_("Text"), {"fields": ["max_length", "min_length"]}), 23 | ( 24 | _("Numbers"), 25 | {"fields": ["max_value", "min_value", "max_digits", "decimal_places"]}, 26 | ), 27 | (_("Regex"), {"fields": ["regex"]}), 28 | (_("Choices"), {"fields": ["choice_values", "choice_labels"]}), 29 | (_("Model Choices"), {"fields": ["choice_model", "choice_model_empty_label"]}), 30 | ] 31 | 32 | 33 | class FormDefinitionAdmin(admin.ModelAdmin): 34 | save_as = True 35 | fieldsets = [ 36 | ( 37 | _("Basic"), 38 | {"fields": ["name", "require_hash", "method", "action", "title", "body"]}, 39 | ), 40 | ( 41 | _("Settings"), 42 | { 43 | "fields": [ 44 | "allow_get_initial", 45 | "log_data", 46 | "success_redirect", 47 | "success_clear", 48 | "display_logged", 49 | "save_uploaded_files", 50 | ], 51 | "classes": ["collapse"], 52 | }, 53 | ), 54 | ( 55 | _("Mail form"), 56 | { 57 | "fields": [ 58 | "mail_to", 59 | "mail_from", 60 | "mail_subject", 61 | "mail_uploaded_files", 62 | "mail_cover_text", 63 | ], 64 | "classes": ["collapse"], 65 | }, 66 | ), 67 | ( 68 | _("Templates"), 69 | { 70 | "fields": ["message_template", "form_template_name"], 71 | "classes": ["collapse"], 72 | }, 73 | ), 74 | ( 75 | _("Messages"), 76 | { 77 | "fields": ["success_message", "error_message", "submit_label"], 78 | "classes": ["collapse"], 79 | }, 80 | ), 81 | ] 82 | list_display = ("name", "title", "method", "count_fields") 83 | form = FormDefinitionForm 84 | inlines = [ 85 | FormDefinitionFieldInline, 86 | ] 87 | search_fields = ("name", "title") 88 | 89 | 90 | class FormLogAdmin(admin.ModelAdmin): 91 | list_display = ("form_definition", "created", "id", "created_by", "data_html") 92 | list_filter = ("form_definition",) 93 | list_display_links = None 94 | date_hierarchy = "created" 95 | 96 | exporter_classes = {} 97 | exporter_classes_ordered = [] 98 | for class_path in settings.EXPORTER_CLASSES: 99 | cls = import_string(class_path) 100 | if cls.is_enabled(): 101 | exporter_classes[cls.export_format()] = cls 102 | exporter_classes_ordered.append(cls) 103 | 104 | def get_exporter_classes(self): 105 | return self.__class__.exporter_classes_ordered 106 | 107 | def get_actions(self, request): 108 | actions = super().get_actions(request) 109 | 110 | for cls in self.get_exporter_classes(): 111 | desc = ( 112 | _("Export selected %%(verbose_name_plural)s as %s") 113 | % cls.export_format() 114 | ) 115 | actions[cls.export_format()] = (cls.export_view, cls.export_format(), desc) 116 | 117 | return actions 118 | 119 | def get_urls(self): 120 | urls = [ 121 | re_path( 122 | r"^export/(?P[a-zA-Z0-9_-]+)/$", 123 | self.admin_site.admin_view(self.export_view), 124 | name="form_designer_export", 125 | ), 126 | ] 127 | return urls + super().get_urls() 128 | 129 | def data_html(self, obj): 130 | return obj.form_definition.compile_message( 131 | obj.data, "html/formdefinition/data_message.html" 132 | ) 133 | 134 | data_html.allow_tags = True 135 | data_html.short_description = _("Data") 136 | 137 | def get_change_list_query_set(self, request, extra_context=None): 138 | """ 139 | The 'change list' admin view for this model. 140 | """ 141 | cl = self.get_changelist_instance(request) 142 | return cl.get_queryset(request) 143 | 144 | def export_view(self, request, format): 145 | queryset = self.get_change_list_query_set(request) 146 | if format not in self.exporter_classes: 147 | raise Http404() 148 | return self.exporter_classes[format](self.model).export(request, queryset) 149 | 150 | def changelist_view(self, request, extra_context=None): 151 | extra_context = extra_context or {} 152 | query_string = "?" + request.META.get("QUERY_STRING", "") 153 | exporter_links = [] 154 | for cls in self.get_exporter_classes(): 155 | url = ( 156 | reverse("admin:form_designer_export", args=(cls.export_format(),)) 157 | + query_string 158 | ) 159 | exporter_links.append( 160 | {"url": url, "label": _("Export view as %s") % cls.export_format()} 161 | ) 162 | 163 | extra_context["exporters"] = exporter_links 164 | 165 | return super().changelist_view(request, extra_context) 166 | 167 | 168 | admin.site.register(FormDefinition, FormDefinitionAdmin) 169 | admin.site.register(FormLog, FormLogAdmin) 170 | -------------------------------------------------------------------------------- /form_designer/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | from django.utils.translation import gettext_lazy as _ 3 | 4 | 5 | class FormDesignerConfig(AppConfig): 6 | name = "form_designer" 7 | verbose_name = _("Form Designer") 8 | -------------------------------------------------------------------------------- /form_designer/contrib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andersinno/django-form-designer-ai/104f205035681e0743372f6f22de96bc6eca9d6c/form_designer/contrib/__init__.py -------------------------------------------------------------------------------- /form_designer/contrib/exporters/__init__.py: -------------------------------------------------------------------------------- 1 | from django.db.models import Count 2 | from django.utils.encoding import force_str 3 | from django.utils.translation import gettext_lazy as _ 4 | 5 | from form_designer import settings 6 | from form_designer.templatetags.friendly import friendly 7 | 8 | 9 | class ExporterBase: 10 | def __init__(self, model): 11 | self.model = model 12 | 13 | @staticmethod 14 | def is_enabled(): 15 | return True 16 | 17 | @staticmethod 18 | def export_format(): 19 | raise NotImplementedError() # pragma: no cover 20 | 21 | def init_writer(self): 22 | raise NotImplementedError() # pragma: no cover 23 | 24 | def init_response(self): 25 | raise NotImplementedError() # pragma: no cover 26 | 27 | def writerow(self, row): 28 | raise NotImplementedError() # pragma: no cover 29 | 30 | def close(self): 31 | pass 32 | 33 | @classmethod 34 | def export_view(cls, modeladmin, request, queryset): 35 | return cls(modeladmin.model).export(request, queryset) 36 | 37 | def export(self, request, queryset=None): 38 | raise NotImplementedError() # pragma: no cover 39 | 40 | 41 | class FormLogExporterBase(ExporterBase): 42 | def export(self, request, queryset=None): # noqa: C901 43 | self.init_response() 44 | self.init_writer() 45 | distinct_forms = queryset.aggregate(Count("form_definition", distinct=True))[ 46 | "form_definition__count" 47 | ] 48 | 49 | include_created = settings.CSV_EXPORT_INCLUDE_CREATED 50 | include_pk = settings.CSV_EXPORT_INCLUDE_PK 51 | include_header = settings.CSV_EXPORT_INCLUDE_HEADER and distinct_forms == 1 52 | include_form = settings.CSV_EXPORT_INCLUDE_FORM and distinct_forms > 1 53 | 54 | if queryset.count(): 55 | fields = queryset[0].form_definition.get_field_dict() 56 | field_order = list(fields.keys()) 57 | if include_header: 58 | header = [] 59 | if include_form: 60 | header.append(_("Form")) 61 | if include_created: 62 | header.append(_("Created")) 63 | if include_pk: 64 | header.append(_("ID")) 65 | # Form fields might have been changed and not match 66 | # existing form logs anymore. 67 | # Hence, use current form definition for header. 68 | # for field in queryset[0].data: 69 | # header.append(field['label'] if field['label'] else field['key']) 70 | for field_name, field in fields.items(): 71 | header.append(field.label or field.name) 72 | 73 | self.writerow( 74 | [ 75 | force_str(cell, encoding=settings.CSV_EXPORT_ENCODING) 76 | for cell in header 77 | ] 78 | ) 79 | 80 | for entry in queryset: 81 | row = [] 82 | if include_form: 83 | row.append(entry.form_definition) 84 | if include_created: 85 | row.append(entry.created) 86 | if include_pk: 87 | row.append(entry.pk) 88 | name_to_value = {d["name"]: d["value"] for d in entry.data} 89 | for field in field_order: 90 | value = friendly( 91 | name_to_value.get(field), 92 | null_value=settings.CSV_EXPORT_NULL_VALUE, 93 | ) 94 | value = force_str(value, encoding=settings.CSV_EXPORT_ENCODING) 95 | row.append(value) 96 | 97 | self.writerow(row) 98 | 99 | self.close() 100 | return self.response 101 | -------------------------------------------------------------------------------- /form_designer/contrib/exporters/csv_exporter.py: -------------------------------------------------------------------------------- 1 | import csv 2 | 3 | from django.http import HttpResponse 4 | 5 | from form_designer import settings 6 | from form_designer.contrib.exporters import FormLogExporterBase 7 | 8 | 9 | class CsvExporter(FormLogExporterBase): 10 | @staticmethod 11 | def export_format(): 12 | return "CSV" 13 | 14 | def init_writer(self): 15 | self.writer = csv.writer(self.response, delimiter=settings.CSV_EXPORT_DELIMITER) 16 | 17 | def init_response(self): 18 | self.response = HttpResponse(content_type="text/csv") 19 | self.response[ 20 | "Content-Disposition" 21 | ] = f"attachment; filename={self.model._meta.verbose_name_plural}.csv" 22 | 23 | def writerow(self, row): 24 | self.writer.writerow(row) 25 | 26 | def export(self, request, queryset=None): 27 | return super().export(request, queryset) 28 | -------------------------------------------------------------------------------- /form_designer/contrib/exporters/xls_exporter.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpResponse 2 | from django.utils.encoding import force_str 3 | 4 | from form_designer.contrib.exporters import FormLogExporterBase 5 | 6 | try: 7 | import xlwt 8 | except ImportError: # pragma: no cover 9 | XLWT_INSTALLED = False 10 | else: # pragma: no cover 11 | XLWT_INSTALLED = True 12 | 13 | 14 | class XlsExporter(FormLogExporterBase): 15 | @staticmethod 16 | def export_format(): 17 | return "XLS" 18 | 19 | @staticmethod 20 | def is_enabled(): 21 | return XLWT_INSTALLED 22 | 23 | def init_writer(self): 24 | self.wb = xlwt.Workbook() 25 | self.ws = self.wb.add_sheet(force_str(self.model._meta.verbose_name_plural)) 26 | self.rownum = 0 27 | 28 | def init_response(self): 29 | self.response = HttpResponse(content_type="application/ms-excel") 30 | self.response["Content-Disposition"] = "attachment; filename=%s.xls" % ( 31 | self.model._meta.verbose_name_plural 32 | ) 33 | 34 | def writerow(self, row): 35 | for i, f in enumerate(row): 36 | self.ws.write(self.rownum, i, force_str(f)) 37 | self.rownum += 1 38 | 39 | def close(self): 40 | self.wb.save(self.response) 41 | 42 | def export(self, request, queryset=None): 43 | return super().export(request, queryset) 44 | -------------------------------------------------------------------------------- /form_designer/email.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from django.core.mail import EmailMessage 4 | from django.utils.encoding import force_str 5 | 6 | from form_designer.utils import string_template_replace 7 | 8 | 9 | def _template_replace_list(input_str, context_dict): 10 | """ 11 | Split the input string by commas or semicolons, then template-replace. 12 | 13 | Falsy input values yield empty lists. 14 | 15 | :param input_str: Comma-or-semicolon-separated list of values 16 | :type input_str: str|None 17 | :param context_dict: The context for template replacement 18 | :return: List of strings 19 | :rtype: list[str] 20 | """ 21 | if not input_str: 22 | return [] 23 | return [ 24 | string_template_replace(email, context_dict) 25 | for email in re.compile(r"\s*[,;]+\s*").split(force_str(input_str)) 26 | ] 27 | 28 | 29 | def build_form_mail(form_definition, form, files=None): 30 | """ 31 | Build a form-submission email based on the given form definition and associated submitted form 32 | 33 | :param form_definition: Form definition object 34 | :param form: The freshly submitted form 35 | :param files: Associated files 36 | :return: Django email message 37 | """ 38 | if not files: 39 | files = [] 40 | form_data = form_definition.get_form_data(form) 41 | message = form_definition.compile_message(form_data) 42 | context_dict = form_definition.get_form_data_context(form_data) 43 | 44 | mail_to = _template_replace_list(form_definition.mail_to, context_dict) 45 | 46 | if form_definition.mail_from: 47 | from_email = string_template_replace(form_definition.mail_from, context_dict) 48 | else: 49 | from_email = None 50 | 51 | reply_to = _template_replace_list(form_definition.mail_reply_to, context_dict) 52 | 53 | mail_subject = string_template_replace( 54 | (form_definition.mail_subject or form_definition.title), context_dict 55 | ) 56 | 57 | kwargs = { 58 | "subject": mail_subject, 59 | "body": message, 60 | "from_email": from_email, 61 | "to": mail_to, 62 | "reply_to": reply_to, 63 | } 64 | 65 | message = EmailMessage(**kwargs) 66 | 67 | if form_definition.is_template_html: 68 | message.content_subtype = "html" 69 | 70 | if form_definition.mail_uploaded_files: 71 | for file_path in files: 72 | message.attach_file(file_path) 73 | 74 | return message 75 | -------------------------------------------------------------------------------- /form_designer/fields.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.apps import apps 3 | from django.core import validators 4 | from django.core.exceptions import ValidationError 5 | from django.db import models 6 | from django.utils.translation import gettext_lazy as _ 7 | 8 | 9 | class ModelNameFormField(forms.CharField): 10 | @staticmethod 11 | def get_model_from_string(model_path): 12 | try: 13 | app_label, model_name = model_path.rsplit(".models.") 14 | return apps.get_model(app_label, model_name) 15 | except (ValueError, LookupError): 16 | return None 17 | 18 | def clean(self, value): 19 | """ 20 | Validates that the input matches the regular expression. Returns a 21 | string object. 22 | """ 23 | value = super().clean(value) 24 | if value in validators.EMPTY_VALUES: 25 | return "" 26 | if not ModelNameFormField.get_model_from_string(value): 27 | raise ValidationError( 28 | _( 29 | "Model could not be imported: %(value)s. Please use a valid model path." 30 | ), 31 | code="invalid", 32 | params={"value": value}, 33 | ) 34 | return value 35 | 36 | 37 | class ModelNameField(models.CharField): 38 | @staticmethod 39 | def get_model_from_string(model_path): 40 | return ModelNameFormField.get_model_from_string(model_path) 41 | 42 | def formfield(self, **kwargs): 43 | # This is a fairly standard way to set up some defaults 44 | # while letting the caller override them. 45 | defaults = {"form_class": ModelNameFormField} 46 | defaults.update(kwargs) 47 | return super().formfield(**defaults) 48 | 49 | 50 | class TemplateFormField(forms.CharField): 51 | def clean(self, value): 52 | """ 53 | Validates that the input can be compiled as a template. 54 | """ 55 | value = super().clean(value) 56 | from django.template import Template, TemplateSyntaxError 57 | 58 | if value: 59 | try: 60 | Template(value) 61 | except TemplateSyntaxError as error: 62 | raise ValidationError(error) 63 | return value 64 | 65 | 66 | class TemplateCharField(models.CharField): 67 | def formfield(self, **kwargs): 68 | # This is a fairly standard way to set up some defaults 69 | # while letting the caller override them. 70 | defaults = {"form_class": TemplateFormField} 71 | defaults.update(kwargs) 72 | return super().formfield(**defaults) 73 | 74 | 75 | class TemplateTextField(models.TextField): 76 | def formfield(self, **kwargs): 77 | # This is a fairly standard way to set up some defaults 78 | # while letting the caller override them. 79 | defaults = {"form_class": TemplateFormField} 80 | defaults.update(kwargs) 81 | return super().formfield(**defaults) 82 | 83 | 84 | class RegexpExpressionFormField(forms.CharField): 85 | def clean(self, value): 86 | """ 87 | Validates that the input can be compiled as a Regular Expression. 88 | """ 89 | value = super().clean(value) 90 | if value in validators.EMPTY_VALUES: 91 | value = "" 92 | import re 93 | 94 | try: 95 | re.compile(value) 96 | except Exception as error: 97 | raise ValidationError(error) 98 | return value 99 | 100 | 101 | class RegexpExpressionField(models.CharField): 102 | def formfield(self, **kwargs): 103 | # This is a fairly standard way to set up some defaults 104 | # while letting the caller override them. 105 | defaults = {"form_class": RegexpExpressionFormField} 106 | defaults.update(kwargs) 107 | return super().formfield(**defaults) 108 | -------------------------------------------------------------------------------- /form_designer/forms.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from django import forms 4 | from django.conf import settings as django_settings 5 | from django.forms import widgets 6 | from django.forms.widgets import Select 7 | from django.utils.module_loading import import_string 8 | from django.utils.translation import gettext_lazy as _ 9 | 10 | from form_designer import settings 11 | from form_designer.models import FormDefinition, FormDefinitionField 12 | from form_designer.uploads import clean_files 13 | 14 | 15 | class DesignedForm(forms.Form): 16 | def __init__(self, form_definition, initial_data=None, *args, **kwargs): 17 | super().__init__(*args, **kwargs) 18 | self.file_fields = [] 19 | for def_field in form_definition.formdefinitionfield_set.all(): 20 | self.add_defined_field(def_field, initial_data) 21 | self.fields[form_definition.submit_flag_name] = forms.BooleanField( 22 | required=False, initial=1, widget=widgets.HiddenInput 23 | ) 24 | 25 | def add_defined_field(self, def_field, initial_data=None): 26 | if initial_data and def_field.name in initial_data: 27 | if def_field.field_class not in ( 28 | "django.forms.MultipleChoiceField", 29 | "django.forms.ModelMultipleChoiceField", 30 | ): 31 | def_field.initial = initial_data.get(def_field.name) 32 | else: 33 | def_field.initial = initial_data.getlist(def_field.name) 34 | field = import_string(def_field.field_class)( 35 | **def_field.get_form_field_init_args() 36 | ) 37 | self.fields[def_field.name] = field 38 | if isinstance(field, forms.FileField): 39 | self.file_fields.append(def_field) 40 | 41 | def clean(self): 42 | return clean_files(self) 43 | 44 | 45 | class FormDefinitionFieldInlineForm(forms.ModelForm): 46 | class Meta: 47 | model = FormDefinitionField 48 | exclude = () 49 | 50 | def clean_regex(self): 51 | if ( 52 | not self.cleaned_data["regex"] 53 | and "field_class" in self.cleaned_data 54 | and self.cleaned_data["field_class"] in ("django.forms.RegexField",) 55 | ): 56 | raise forms.ValidationError( 57 | _("This field class requires a regular expression.") 58 | ) 59 | return self.cleaned_data["regex"] 60 | 61 | def clean_choice_model(self): 62 | if ( 63 | not self.cleaned_data["choice_model"] 64 | and "field_class" in self.cleaned_data 65 | and self.cleaned_data["field_class"] 66 | in ( 67 | "django.forms.ModelChoiceField", 68 | "django.forms.ModelMultipleChoiceField", 69 | ) 70 | ): 71 | raise forms.ValidationError(_("This field class requires a model.")) 72 | return self.cleaned_data["choice_model"] 73 | 74 | def __init__(self, data=None, files=None, **kwargs): 75 | super().__init__(data=data, files=files, **kwargs) 76 | for field_name, choices in ( 77 | ("field_class", settings.FIELD_CLASSES), 78 | ("widget", settings.WIDGET_CLASSES), 79 | ("choice_model", settings.CHOICE_MODEL_CHOICES), 80 | ): 81 | if choices is None: 82 | continue 83 | if field_name in self.fields: 84 | self.fields[field_name].widget = Select(choices=choices) 85 | if not self.initial.get(field_name): 86 | self.initial[field_name] = choices[0][0] 87 | 88 | 89 | class FormDefinitionForm(forms.ModelForm): 90 | class Meta: 91 | model = FormDefinition 92 | exclude = () 93 | 94 | def _media(self): 95 | js = [] 96 | plugins = [ 97 | "js/jquery-ui.js", 98 | "js/jquery-inline-positioning.js", 99 | "js/jquery-inline-rename.js", 100 | "js/jquery-inline-collapsible.js", 101 | "js/jquery-inline-fieldset-collapsible.js", 102 | "js/jquery-inline-prepopulate-label.js", 103 | ] 104 | if hasattr(django_settings, "JQUERY_URL"): 105 | js.append(django_settings.JQUERY_URL) 106 | else: 107 | plugins = ["js/jquery.js"] + plugins 108 | js.extend([os.path.join(settings.STATIC_URL, path) for path in plugins]) 109 | return forms.Media(js=js) 110 | 111 | media = property(_media) 112 | 113 | def __init__(self, data=None, files=None, **kwargs): 114 | super().__init__(data=data, files=files, **kwargs) 115 | if "form_template_name" in self.fields: 116 | self.fields["form_template_name"].widget = Select( 117 | choices=settings.FORM_TEMPLATES 118 | ) 119 | -------------------------------------------------------------------------------- /form_designer/locale/de/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andersinno/django-form-designer-ai/104f205035681e0743372f6f22de96bc6eca9d6c/form_designer/locale/de/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /form_designer/locale/de/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: PACKAGE VERSION\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2012-01-19 20:56-0500\n" 11 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 12 | "Last-Translator: FULL NAME \n" 13 | "Language-Team: LANGUAGE \n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | 18 | #: admin.py:27 admin.py:38 19 | msgid "Basic" 20 | msgstr "Grundlegendes" 21 | 22 | #: admin.py:28 23 | msgid "Display" 24 | msgstr "Darstellung" 25 | 26 | #: admin.py:29 settings.py:10 27 | msgid "Text" 28 | msgstr "Text" 29 | 30 | #: admin.py:30 31 | msgid "Numbers" 32 | msgstr "Zahlen" 33 | 34 | #: admin.py:31 settings.py:23 35 | msgid "Regex" 36 | msgstr "Regex" 37 | 38 | #: admin.py:32 39 | msgid "Choices" 40 | msgstr "Auswahl" 41 | 42 | #: admin.py:33 43 | msgid "Model Choices" 44 | msgstr "Modell-Auswahl" 45 | 46 | #: admin.py:39 47 | msgid "Settings" 48 | msgstr "" 49 | 50 | #: admin.py:40 51 | msgid "Mail form" 52 | msgstr "Mail-Formular" 53 | 54 | #: admin.py:41 55 | msgid "Templates" 56 | msgstr "Vorlagen" 57 | 58 | #: admin.py:42 59 | msgid "Messages" 60 | msgstr "Meldungen" 61 | 62 | #: admin.py:63 admin.py:102 admin.py:147 models.py:60 models.py:173 63 | #: contrib/cms_plugins/form_designer_form/cms_plugins.py:14 64 | msgid "Form" 65 | msgstr "Formular" 66 | 67 | #: admin.py:78 models.py:174 68 | msgid "Data" 69 | msgstr "Daten" 70 | 71 | #: admin.py:104 admin.py:149 models.py:172 72 | msgid "Created" 73 | msgstr "Erstellt" 74 | 75 | #: admin.py:106 admin.py:151 76 | msgid "ID" 77 | msgstr "ID" 78 | 79 | #: admin.py:125 80 | #, python-format 81 | msgid "Export selected %(verbose_name_plural)s as CSV" 82 | msgstr "Ausgewählte %(verbose_name_plural)s als CSV exportieren" 83 | 84 | #: admin.py:173 85 | #, python-format 86 | msgid "Export selected %(verbose_name_plural)s as XLS" 87 | msgstr "Ausgewählte %(verbose_name_plural)s als XLS exportieren" 88 | 89 | #: admin.py:185 90 | msgid "CSV export is not enabled." 91 | msgstr "CSV-Export ist nicht aktiviert." 92 | 93 | #: admin.py:190 94 | msgid "XLS export is not enabled." 95 | msgstr "XLS-Export ist nicht aktiviert." 96 | 97 | #: forms.py:43 98 | msgid "This field class requires a regular expression." 99 | msgstr "Diese Feld-Klasse benötigt einen Regulären Ausdruck." 100 | 101 | #: forms.py:48 102 | msgid "This field class requires a model." 103 | msgstr "Diese Feld-Klasse benötigt ein Modell." 104 | 105 | #: models.py:35 models.py:187 106 | msgid "name" 107 | msgstr "Name" 108 | 109 | #: models.py:36 110 | msgid "obfuscate URL to this form" 111 | msgstr "" 112 | 113 | #: models.py:36 114 | msgid "If enabled, the form can only be reached via a secret URL." 115 | msgstr "" 116 | 117 | #: models.py:39 118 | msgid "title" 119 | msgstr "Titel" 120 | 121 | #: models.py:40 122 | msgid "body" 123 | msgstr "Text" 124 | 125 | #: models.py:41 126 | msgid "target URL" 127 | msgstr "Ziel-URL" 128 | 129 | #: models.py:41 130 | msgid "" 131 | "If you leave this empty, the page where the form resides will be requested, " 132 | "and you can use the mail form and logging features. You can also send data " 133 | "to external sites: For instance, enter \"http://www.google.ch/search\" to " 134 | "create a search form." 135 | msgstr "" 136 | "Falls leer, wird die Seite angefragt, auf welcher sich das Formular " 137 | "befindet, und die Mail-Formular- und Logging-Funktionen können verwendet " 138 | "werden. Es ist auch möglich, Daten an externe Sites zu senden: Beispielweise " 139 | "\"http://www.google.ch/search\" eingeben, um ein Suchformular zu erstellen." 140 | 141 | #: models.py:42 142 | msgid "send form data to e-mail address" 143 | msgstr "Formulardaten an E-Mail-Adresse senden" 144 | 145 | #: models.py:43 146 | msgid "sender address" 147 | msgstr "Absender-Adresse" 148 | 149 | #: models.py:44 150 | msgid "email subject" 151 | msgstr "E-Mail-Betreff" 152 | 153 | #: models.py:45 154 | msgid "Send uploaded files as email attachments" 155 | msgstr "" 156 | 157 | #: models.py:46 158 | msgid "method" 159 | msgstr "Methode" 160 | 161 | #: models.py:47 162 | msgid "success message" 163 | msgstr "Erfolgsmeldung" 164 | 165 | #: models.py:48 166 | msgid "error message" 167 | msgstr "Fehlermeldung" 168 | 169 | #: models.py:49 170 | msgid "submit button label" 171 | msgstr "Beschriftung der Senden-Schaltfläche" 172 | 173 | #: models.py:50 174 | msgid "log form data" 175 | msgstr "Formulardaten loggen" 176 | 177 | #: models.py:50 178 | msgid "Logs all form submissions to the database." 179 | msgstr "Speichert alle Formular-Übermittlungen in der Datenbank." 180 | 181 | #: models.py:51 182 | msgid "save uploaded files" 183 | msgstr "" 184 | 185 | #: models.py:51 186 | msgid "Saves all uploaded files using server storage." 187 | msgstr "" 188 | 189 | #: models.py:52 190 | msgid "HTTP redirect after successful submission" 191 | msgstr "HTTP-Weiterleitung nach erfolgreicher Übermittlung" 192 | 193 | #: models.py:53 194 | msgid "clear form after successful submission" 195 | msgstr "Formular zurücksetzen nach erfolgreicher Übermittlung" 196 | 197 | #: models.py:54 198 | msgid "allow initial values via URL" 199 | msgstr "Initialwerte via URL erlauben" 200 | 201 | #: models.py:54 202 | msgid "" 203 | "If enabled, you can fill in form fields by adding them to the query string." 204 | msgstr "" 205 | "Falls aktiviert, ist es möglich, Formularfelder vorauszufüllen, indem sie " 206 | "dem Query-String hinzugefügt werden." 207 | 208 | #: models.py:55 209 | msgid "message template" 210 | msgstr "Nachrichten-Vorlage" 211 | 212 | #: models.py:55 213 | msgid "" 214 | "Your form fields are available as template context. Example: \"{{ message }}" 215 | "\" if you have a field named `message`. To iterate over all fields, use the " 216 | "variable `data` (a list containing a dictionary for each form field, each " 217 | "containing the elements `name`, `label`, `value`)." 218 | msgstr "" 219 | 220 | #: models.py:56 221 | msgid "form template" 222 | msgstr "Formular-Vorlage" 223 | 224 | #: models.py:57 225 | msgid "display logged submissions with form" 226 | msgstr "Geloggte Formulardaten mit Formular anzeigen" 227 | 228 | #: models.py:61 229 | msgid "Forms" 230 | msgstr "Formulare" 231 | 232 | #: models.py:116 233 | msgid "Fields" 234 | msgstr "Felder" 235 | 236 | #: models.py:177 237 | msgid "Form log" 238 | msgstr "Formular-Log" 239 | 240 | #: models.py:178 241 | msgid "Form logs" 242 | msgstr "Formular-Logs" 243 | 244 | #: models.py:184 245 | msgid "field class" 246 | msgstr "Feld-Klasse" 247 | 248 | #: models.py:185 249 | msgid "position" 250 | msgstr "Position" 251 | 252 | #: models.py:188 253 | msgid "label" 254 | msgstr "Beschriftung" 255 | 256 | #: models.py:189 257 | msgid "required" 258 | msgstr "Erforderlich" 259 | 260 | #: models.py:190 261 | msgid "include in result" 262 | msgstr "In Resultat einbeziehen" 263 | 264 | #: models.py:191 265 | msgid "widget" 266 | msgstr "Widget" 267 | 268 | #: models.py:192 269 | msgid "initial value" 270 | msgstr "Initialwert" 271 | 272 | #: models.py:193 273 | msgid "help text" 274 | msgstr "Hilfetext" 275 | 276 | #: models.py:195 277 | msgid "values" 278 | msgstr "Werte" 279 | 280 | #: models.py:195 281 | msgid "One value per line" 282 | msgstr "Ein Wert pro Zeile" 283 | 284 | #: models.py:196 285 | msgid "labels" 286 | msgstr "Beschriftungen" 287 | 288 | #: models.py:196 289 | msgid "One label per line" 290 | msgstr "Eine Beschriftung pro Zeile" 291 | 292 | #: models.py:198 293 | msgid "max. length" 294 | msgstr "Max. Länge" 295 | 296 | #: models.py:199 297 | msgid "min. length" 298 | msgstr "Min. Länge" 299 | 300 | #: models.py:200 301 | msgid "max. value" 302 | msgstr "Max. Wert" 303 | 304 | #: models.py:201 305 | msgid "min. value" 306 | msgstr "Min. Wert" 307 | 308 | #: models.py:202 309 | msgid "max. digits" 310 | msgstr "Max. Ziffern" 311 | 312 | #: models.py:203 313 | msgid "decimal places" 314 | msgstr "Dezimalstellen" 315 | 316 | #: models.py:205 317 | msgid "regular Expression" 318 | msgstr "Regulärer Ausdruck" 319 | 320 | #: models.py:208 321 | msgid "data model" 322 | msgstr "Datenmodell" 323 | 324 | #: models.py:209 325 | msgid "empty label" 326 | msgstr "Beschriftung falls leer" 327 | 328 | #: models.py:212 329 | msgid "field" 330 | msgstr "Feld" 331 | 332 | #: models.py:213 333 | msgid "fields" 334 | msgstr "Felder" 335 | 336 | #: settings.py:11 337 | msgid "E-mail address" 338 | msgstr "E-Mail-Adresse" 339 | 340 | #: settings.py:12 341 | msgid "Web address" 342 | msgstr "Web-Adresse" 343 | 344 | #: settings.py:13 345 | msgid "Number" 346 | msgstr "Zahl" 347 | 348 | #: settings.py:14 349 | msgid "Decimal number" 350 | msgstr "Dezimalzahl" 351 | 352 | #: settings.py:15 353 | msgid "Yes/No" 354 | msgstr "Ja/Nein" 355 | 356 | #: settings.py:16 357 | msgid "Date" 358 | msgstr "Datum" 359 | 360 | #: settings.py:17 361 | msgid "Date & time" 362 | msgstr "Datum & Zeit" 363 | 364 | #: settings.py:18 365 | msgid "Time" 366 | msgstr "Zeit" 367 | 368 | #: settings.py:19 369 | msgid "Choice" 370 | msgstr "Auswahl" 371 | 372 | #: settings.py:20 373 | msgid "Multiple Choice" 374 | msgstr "Mehrfach-Auswahl" 375 | 376 | #: settings.py:21 377 | msgid "Model Choice" 378 | msgstr "Modell-Auswahl" 379 | 380 | #: settings.py:22 381 | msgid "Model Multiple Choice" 382 | msgstr "Modell-Mehrfach-Auswahl" 383 | 384 | #: settings.py:24 385 | msgid "File" 386 | msgstr "Datei" 387 | 388 | #: settings.py:29 settings.py:37 389 | msgid "Default" 390 | msgstr "Voreinstellung" 391 | 392 | #: settings.py:30 393 | msgid "Text area" 394 | msgstr "Textfeld" 395 | 396 | #: settings.py:31 397 | msgid "Password input" 398 | msgstr "Passwortfeld" 399 | 400 | #: settings.py:32 401 | msgid "Hidden input" 402 | msgstr "Versteckter Wert" 403 | 404 | #: settings.py:33 405 | msgid "Radio button" 406 | msgstr "Radio-Buttons" 407 | 408 | #: settings.py:38 409 | msgid "as paragraphs" 410 | msgstr "als Absätze" 411 | 412 | #: settings.py:39 413 | msgid "as table" 414 | msgstr "als Tabelle" 415 | 416 | #: settings.py:40 417 | msgid "as table (horizontal)" 418 | msgstr "als Tabelle (horizontal)" 419 | 420 | #: settings.py:41 421 | msgid "as unordered list" 422 | msgstr "als unsortierte Liste" 423 | 424 | #: settings.py:42 425 | msgid "custom implementation" 426 | msgstr "eigene Implementierung" 427 | 428 | #: uploads.py:20 429 | msgid "This field is required." 430 | msgstr "Diese Feld-Klasse benötigt ein Modell." 431 | 432 | #: uploads.py:25 433 | msgid "This file type is not allowed." 434 | msgstr "" 435 | 436 | #: uploads.py:27 437 | #, python-format 438 | msgid "Please keep file size under %(max_size)s. Current size is %(size)s." 439 | msgstr "" 440 | 441 | #: views.py:21 442 | msgid "Thank you, the data was submitted successfully." 443 | msgstr "Vielen Dank, die Daten wurden übermittelt." 444 | 445 | #: views.py:22 446 | msgid "The data could not be submitted, please try again." 447 | msgstr "Die Daten konnten nicht übermittelt werden, bitte erneut versuchen." 448 | 449 | #: contrib/cms_plugins/form_designer_form/cms_plugins.py:13 450 | msgid "Form Designer" 451 | msgstr "" 452 | 453 | #: contrib/cms_plugins/form_designer_form/models.py:8 454 | msgid "form" 455 | msgstr "Formular" 456 | 457 | #: templates/admin/form_designer/formlog/change_list.html:9 458 | msgid "Export CSV" 459 | msgstr "CSV exportieren" 460 | 461 | #: templates/admin/form_designer/formlog/change_list.html:16 462 | msgid "Export XLS" 463 | msgstr "XLS exportieren" 464 | 465 | #: templates/html/formdefinition/forms/includes/submit.html:2 466 | msgid "Submit" 467 | msgstr "Absenden" 468 | 469 | #: templatetags/friendly.py:18 470 | msgid "yes" 471 | msgstr "ja" 472 | 473 | #: templatetags/friendly.py:18 474 | msgid "no" 475 | msgstr "nein" 476 | -------------------------------------------------------------------------------- /form_designer/locale/en/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andersinno/django-form-designer-ai/104f205035681e0743372f6f22de96bc6eca9d6c/form_designer/locale/en/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /form_designer/locale/en/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: PACKAGE VERSION\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2012-01-19 20:56-0500\n" 11 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 12 | "Last-Translator: FULL NAME \n" 13 | "Language-Team: LANGUAGE \n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | 18 | #: admin.py:27 admin.py:38 19 | msgid "Basic" 20 | msgstr "" 21 | 22 | #: admin.py:28 23 | msgid "Display" 24 | msgstr "" 25 | 26 | #: admin.py:29 settings.py:10 27 | msgid "Text" 28 | msgstr "" 29 | 30 | #: admin.py:30 31 | msgid "Numbers" 32 | msgstr "" 33 | 34 | #: admin.py:31 settings.py:23 35 | msgid "Regex" 36 | msgstr "" 37 | 38 | #: admin.py:32 39 | msgid "Choices" 40 | msgstr "" 41 | 42 | #: admin.py:33 43 | msgid "Model Choices" 44 | msgstr "" 45 | 46 | #: admin.py:39 47 | msgid "Settings" 48 | msgstr "" 49 | 50 | #: admin.py:40 51 | msgid "Mail form" 52 | msgstr "" 53 | 54 | #: admin.py:41 55 | msgid "Templates" 56 | msgstr "" 57 | 58 | #: admin.py:42 59 | msgid "Messages" 60 | msgstr "" 61 | 62 | #: admin.py:63 admin.py:102 admin.py:147 models.py:60 models.py:173 63 | #: contrib/cms_plugins/form_designer_form/cms_plugins.py:14 64 | msgid "Form" 65 | msgstr "" 66 | 67 | #: admin.py:78 models.py:174 68 | msgid "Data" 69 | msgstr "" 70 | 71 | #: admin.py:104 admin.py:149 models.py:172 72 | msgid "Created" 73 | msgstr "" 74 | 75 | #: admin.py:106 admin.py:151 76 | msgid "ID" 77 | msgstr "" 78 | 79 | #: admin.py:125 80 | #, python-format 81 | msgid "Export selected %(verbose_name_plural)s as CSV" 82 | msgstr "" 83 | 84 | #: admin.py:173 85 | #, python-format 86 | msgid "Export selected %(verbose_name_plural)s as XLS" 87 | msgstr "" 88 | 89 | #: admin.py:185 90 | msgid "CSV export is not enabled." 91 | msgstr "" 92 | 93 | #: admin.py:190 94 | msgid "XLS export is not enabled." 95 | msgstr "" 96 | 97 | #: forms.py:43 98 | msgid "This field class requires a regular expression." 99 | msgstr "" 100 | 101 | #: forms.py:48 102 | msgid "This field class requires a model." 103 | msgstr "" 104 | 105 | #: models.py:35 models.py:187 106 | msgid "name" 107 | msgstr "" 108 | 109 | #: models.py:36 110 | msgid "obfuscate URL to this form" 111 | msgstr "" 112 | 113 | #: models.py:36 114 | msgid "If enabled, the form can only be reached via a secret URL." 115 | msgstr "" 116 | 117 | #: models.py:39 118 | msgid "title" 119 | msgstr "" 120 | 121 | #: models.py:40 122 | msgid "body" 123 | msgstr "" 124 | 125 | #: models.py:41 126 | msgid "target URL" 127 | msgstr "" 128 | 129 | #: models.py:41 130 | msgid "" 131 | "If you leave this empty, the page where the form resides will be requested, " 132 | "and you can use the mail form and logging features. You can also send data " 133 | "to external sites: For instance, enter \"http://www.google.ch/search\" to " 134 | "create a search form." 135 | msgstr "" 136 | 137 | #: models.py:42 138 | msgid "send form data to e-mail address" 139 | msgstr "" 140 | 141 | #: models.py:43 142 | msgid "sender address" 143 | msgstr "" 144 | 145 | #: models.py:44 146 | msgid "email subject" 147 | msgstr "" 148 | 149 | #: models.py:45 150 | msgid "Send uploaded files as email attachments" 151 | msgstr "" 152 | 153 | #: models.py:46 154 | msgid "method" 155 | msgstr "" 156 | 157 | #: models.py:47 158 | msgid "success message" 159 | msgstr "" 160 | 161 | #: models.py:48 162 | msgid "error message" 163 | msgstr "" 164 | 165 | #: models.py:49 166 | msgid "submit button label" 167 | msgstr "" 168 | 169 | #: models.py:50 170 | msgid "log form data" 171 | msgstr "" 172 | 173 | #: models.py:50 174 | msgid "Logs all form submissions to the database." 175 | msgstr "" 176 | 177 | #: models.py:51 178 | msgid "save uploaded files" 179 | msgstr "" 180 | 181 | #: models.py:51 182 | msgid "Saves all uploaded files using server storage." 183 | msgstr "" 184 | 185 | #: models.py:52 186 | msgid "HTTP redirect after successful submission" 187 | msgstr "" 188 | 189 | #: models.py:53 190 | msgid "clear form after successful submission" 191 | msgstr "" 192 | 193 | #: models.py:54 194 | msgid "allow initial values via URL" 195 | msgstr "" 196 | 197 | #: models.py:54 198 | msgid "" 199 | "If enabled, you can fill in form fields by adding them to the query string." 200 | msgstr "" 201 | 202 | #: models.py:55 203 | msgid "message template" 204 | msgstr "" 205 | 206 | #: models.py:55 207 | msgid "" 208 | "Your form fields are available as template context. Example: \"{{ message }}" 209 | "\" if you have a field named `message`. To iterate over all fields, use the " 210 | "variable `data` (a list containing a dictionary for each form field, each " 211 | "containing the elements `name`, `label`, `value`)." 212 | msgstr "" 213 | 214 | #: models.py:56 215 | msgid "form template" 216 | msgstr "" 217 | 218 | #: models.py:57 219 | msgid "display logged submissions with form" 220 | msgstr "" 221 | 222 | #: models.py:61 223 | msgid "Forms" 224 | msgstr "" 225 | 226 | #: models.py:116 227 | msgid "Fields" 228 | msgstr "" 229 | 230 | #: models.py:177 231 | msgid "Form log" 232 | msgstr "" 233 | 234 | #: models.py:178 235 | msgid "Form logs" 236 | msgstr "" 237 | 238 | #: models.py:184 239 | msgid "field class" 240 | msgstr "" 241 | 242 | #: models.py:185 243 | msgid "position" 244 | msgstr "" 245 | 246 | #: models.py:188 247 | msgid "label" 248 | msgstr "" 249 | 250 | #: models.py:189 251 | msgid "required" 252 | msgstr "" 253 | 254 | #: models.py:190 255 | msgid "include in result" 256 | msgstr "" 257 | 258 | #: models.py:191 259 | msgid "widget" 260 | msgstr "" 261 | 262 | #: models.py:192 263 | msgid "initial value" 264 | msgstr "" 265 | 266 | #: models.py:193 267 | msgid "help text" 268 | msgstr "" 269 | 270 | #: models.py:195 271 | msgid "values" 272 | msgstr "" 273 | 274 | #: models.py:195 275 | msgid "One value per line" 276 | msgstr "" 277 | 278 | #: models.py:196 279 | msgid "labels" 280 | msgstr "" 281 | 282 | #: models.py:196 283 | msgid "One label per line" 284 | msgstr "" 285 | 286 | #: models.py:198 287 | msgid "max. length" 288 | msgstr "" 289 | 290 | #: models.py:199 291 | msgid "min. length" 292 | msgstr "" 293 | 294 | #: models.py:200 295 | msgid "max. value" 296 | msgstr "" 297 | 298 | #: models.py:201 299 | msgid "min. value" 300 | msgstr "" 301 | 302 | #: models.py:202 303 | msgid "max. digits" 304 | msgstr "" 305 | 306 | #: models.py:203 307 | msgid "decimal places" 308 | msgstr "" 309 | 310 | #: models.py:205 311 | msgid "regular Expression" 312 | msgstr "" 313 | 314 | #: models.py:208 315 | msgid "data model" 316 | msgstr "" 317 | 318 | #: models.py:209 319 | msgid "empty label" 320 | msgstr "" 321 | 322 | #: models.py:212 323 | msgid "field" 324 | msgstr "" 325 | 326 | #: models.py:213 327 | msgid "fields" 328 | msgstr "" 329 | 330 | #: settings.py:11 331 | msgid "E-mail address" 332 | msgstr "" 333 | 334 | #: settings.py:12 335 | msgid "Web address" 336 | msgstr "" 337 | 338 | #: settings.py:13 339 | msgid "Number" 340 | msgstr "" 341 | 342 | #: settings.py:14 343 | msgid "Decimal number" 344 | msgstr "" 345 | 346 | #: settings.py:15 347 | msgid "Yes/No" 348 | msgstr "" 349 | 350 | #: settings.py:16 351 | msgid "Date" 352 | msgstr "" 353 | 354 | #: settings.py:17 355 | msgid "Date & time" 356 | msgstr "" 357 | 358 | #: settings.py:18 359 | msgid "Time" 360 | msgstr "" 361 | 362 | #: settings.py:19 363 | msgid "Choice" 364 | msgstr "" 365 | 366 | #: settings.py:20 367 | msgid "Multiple Choice" 368 | msgstr "" 369 | 370 | #: settings.py:21 371 | msgid "Model Choice" 372 | msgstr "" 373 | 374 | #: settings.py:22 375 | msgid "Model Multiple Choice" 376 | msgstr "" 377 | 378 | #: settings.py:24 379 | msgid "File" 380 | msgstr "" 381 | 382 | #: settings.py:29 settings.py:37 383 | msgid "Default" 384 | msgstr "" 385 | 386 | #: settings.py:30 387 | msgid "Text area" 388 | msgstr "" 389 | 390 | #: settings.py:31 391 | msgid "Password input" 392 | msgstr "" 393 | 394 | #: settings.py:32 395 | msgid "Hidden input" 396 | msgstr "" 397 | 398 | #: settings.py:33 399 | msgid "Radio button" 400 | msgstr "" 401 | 402 | #: settings.py:38 403 | msgid "as paragraphs" 404 | msgstr "" 405 | 406 | #: settings.py:39 407 | msgid "as table" 408 | msgstr "" 409 | 410 | #: settings.py:40 411 | msgid "as table (horizontal)" 412 | msgstr "" 413 | 414 | #: settings.py:41 415 | msgid "as unordered list" 416 | msgstr "" 417 | 418 | #: settings.py:42 419 | msgid "custom implementation" 420 | msgstr "" 421 | 422 | #: uploads.py:20 423 | msgid "This field is required." 424 | msgstr "" 425 | 426 | #: uploads.py:25 427 | msgid "This file type is not allowed." 428 | msgstr "" 429 | 430 | #: uploads.py:27 431 | #, python-format 432 | msgid "Please keep file size under %(max_size)s. Current size is %(size)s." 433 | msgstr "" 434 | 435 | #: views.py:21 436 | msgid "Thank you, the data was submitted successfully." 437 | msgstr "" 438 | 439 | #: views.py:22 440 | msgid "The data could not be submitted, please try again." 441 | msgstr "" 442 | 443 | #: contrib/cms_plugins/form_designer_form/cms_plugins.py:13 444 | msgid "Form Designer" 445 | msgstr "" 446 | 447 | #: contrib/cms_plugins/form_designer_form/models.py:8 448 | msgid "form" 449 | msgstr "" 450 | 451 | #: templates/admin/form_designer/formlog/change_list.html:9 452 | msgid "Export CSV" 453 | msgstr "" 454 | 455 | #: templates/admin/form_designer/formlog/change_list.html:16 456 | msgid "Export XLS" 457 | msgstr "" 458 | 459 | #: templates/html/formdefinition/forms/includes/submit.html:2 460 | msgid "Submit" 461 | msgstr "" 462 | 463 | #: templatetags/friendly.py:18 464 | msgid "yes" 465 | msgstr "" 466 | 467 | #: templatetags/friendly.py:18 468 | msgid "no" 469 | msgstr "" 470 | 471 | #: views.py:50 472 | msgid "Invalid reCAPTCHA." 473 | msgstr "" 474 | -------------------------------------------------------------------------------- /form_designer/locale/es/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andersinno/django-form-designer-ai/104f205035681e0743372f6f22de96bc6eca9d6c/form_designer/locale/es/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /form_designer/locale/es/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: PACKAGE VERSION\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2012-03-28 14:57+0200\n" 11 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 12 | "Last-Translator: FULL NAME \n" 13 | "Language-Team: Spanish , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: PACKAGE VERSION\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2012-09-28 11:05+0300\n" 11 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 12 | "Last-Translator: FULL NAME \n" 13 | "Language-Team: 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 | 19 | #: .\admin.py:18 .\admin.py:30 20 | msgid "Basic" 21 | msgstr "Perus" 22 | 23 | #: .\admin.py:19 24 | msgid "Display" 25 | msgstr "Näyttöasetukset" 26 | 27 | #: .\admin.py:20 .\settings.py:10 28 | msgid "Text" 29 | msgstr "Tekstiasetukset" 30 | 31 | #: .\admin.py:21 32 | msgid "Numbers" 33 | msgstr "Numeroasetukset" 34 | 35 | #: .\admin.py:22 .\settings.py:23 36 | msgid "Regex" 37 | msgstr "Säännöllinen lauseke" 38 | 39 | #: .\admin.py:23 40 | msgid "Choices" 41 | msgstr "Valinnat" 42 | 43 | #: .\admin.py:24 44 | msgid "Model Choices" 45 | msgstr "Tietokantavalinnat" 46 | 47 | #: .\admin.py:31 48 | msgid "Settings" 49 | msgstr "Asetukset" 50 | 51 | #: .\admin.py:32 52 | msgid "Mail form" 53 | msgstr "Sähköpostiasetukset" 54 | 55 | #: .\admin.py:33 56 | msgid "Templates" 57 | msgstr "Asettelupohjat" 58 | 59 | #: .\admin.py:34 60 | msgid "Messages" 61 | msgstr "Viestit" 62 | 63 | #: .\admin.py:63 64 | #, python-format 65 | msgid "Export selected %%(verbose_name_plural)s as %s" 66 | msgstr "Vie valitut %%(verbose_name_plural)s %s-muotoon" 67 | 68 | #: .\admin.py:73 .\models.py:55 69 | #: .\contrib\cms_plugins\form_designer_form\cms_plugins.py:14 70 | #: .\contrib\exporters\__init__.py:58 71 | msgid "Form" 72 | msgstr "Lomake" 73 | 74 | #: .\admin.py:84 75 | msgid "Data" 76 | msgstr "Data" 77 | 78 | #: .\admin.py:108 79 | #, python-format 80 | msgid "Export view as %s" 81 | msgstr "Vie näkymä %s-muotoon" 82 | 83 | #: .\forms.py:44 84 | msgid "This field class requires a regular expression." 85 | msgstr "Tämä kenttätyyppi vaatii säännöllisen lausekkeen." 86 | 87 | #: .\forms.py:49 88 | msgid "This field class requires a model." 89 | msgstr "Tämä kenttätyyppi vaatii tietokantamallin." 90 | 91 | #: .\models.py:30 .\models.py:180 92 | msgid "name" 93 | msgstr "nimi" 94 | 95 | #: .\models.py:31 96 | msgid "obfuscate URL to this form" 97 | msgstr "piilota tämän lomakkeen URL-osoite" 98 | 99 | #: .\models.py:31 100 | msgid "If enabled, the form can only be reached via a secret URL." 101 | msgstr "Lomakkeeseen pääsee vain salaisesta URL-osoitteesta mikäli tämä kohta on valittu." 102 | 103 | #: .\models.py:34 104 | msgid "title" 105 | msgstr "otsikko" 106 | 107 | #: .\models.py:35 108 | msgid "body" 109 | msgstr "sisältö" 110 | 111 | #: .\models.py:36 112 | msgid "target URL" 113 | msgstr "kohde-URL" 114 | 115 | #: .\models.py:36 116 | msgid "" 117 | "If you leave this empty, the page where the form resides will be requested, " 118 | "and you can use the mail form and logging features. You can also send data " 119 | "to external sites: For instance, enter \"http://www.google.ch/search\" to " 120 | "create a search form." 121 | msgstr "" 122 | "Mikäli jätät tämän tyhjäksi, voit käyttää sähköpostiasetuksia ja lokitoiminnallisuutta." 123 | "Voit myös lähettää lomakkeen tiedot ulkoisille sivustoille; syöttämällä esimerkiksi \"http://www.google.fi/search\" " 124 | "voit luoda hakulomakkeen." 125 | 126 | #: .\models.py:37 127 | msgid "send form data to e-mail address" 128 | msgstr "lähetä lomakkeen tiedot sähköpostitse" 129 | 130 | #: .\models.py:38 131 | msgid "sender address" 132 | msgstr "lähettäjän osoite" 133 | 134 | #: .\models.py:39 135 | msgid "email subject" 136 | msgstr "sähköpostiviestin aihe" 137 | 138 | #: .\models.py:40 139 | msgid "Send uploaded files as email attachments" 140 | msgstr "Liitä lähetetyt tiedostot sähköpostiviesteihin liitetiedostoina" 141 | 142 | #: .\models.py:41 143 | msgid "method" 144 | msgstr "metodi" 145 | 146 | #: .\models.py:42 147 | msgid "success message" 148 | msgstr "kiitosviesti" 149 | 150 | #: .\models.py:43 151 | msgid "error message" 152 | msgstr "virheviesti" 153 | 154 | #: .\models.py:44 155 | msgid "submit button label" 156 | msgstr "lähetyspainikkeen teksti" 157 | 158 | #: .\models.py:45 159 | msgid "log form data" 160 | msgstr "käytä lokia" 161 | 162 | #: .\models.py:45 163 | msgid "Logs all form submissions to the database." 164 | msgstr "Tallentaa kaikki lähetetyt lomakkeet tietokantaan." 165 | 166 | #: .\models.py:46 167 | msgid "save uploaded files" 168 | msgstr "tallenna lähetetyt tiedostot" 169 | 170 | #: .\models.py:46 171 | msgid "Saves all uploaded files using server storage." 172 | msgstr "Tallentaa kaikki lähetetyt tiedostot palvelimelle." 173 | 174 | #: .\models.py:47 175 | msgid "HTTP redirect after successful submission" 176 | msgstr "HTTP-uudelleenohjaus onnistuneen lähetyksen jälkeen" 177 | 178 | #: .\models.py:48 179 | msgid "clear form after successful submission" 180 | msgstr "tyhjennä lomake onnistuneen lähetyksen jälkeen" 181 | 182 | #: .\models.py:49 183 | msgid "allow initial values via URL" 184 | msgstr "salli oletusarvot URL-osoitteessa" 185 | 186 | #: .\models.py:49 187 | msgid "" 188 | "If enabled, you can fill in form fields by adding them to the query string." 189 | msgstr "" 190 | "Mikäli tämä on valittu voit täyttää kenttiin oletusarvot käyttämällä URL:n kyselyosaa." 191 | 192 | #: .\models.py:50 193 | msgid "message template" 194 | msgstr "viestipohja" 195 | 196 | #: .\models.py:50 197 | msgid "" 198 | "Your form fields are available as template context. Example: \"{{ message }}" 199 | "\" if you have a field named `message`. To iterate over all fields, use the " 200 | "variable `data` (a list containing a dictionary for each form field, each " 201 | "containing the elements `name`, `label`, `value`)." 202 | msgstr "" 203 | "Lomakkeen kentät ovat käytettävissä. Saat esimerkiksi \"viesti\"-nimisen kentän " 204 | "viestipohjaan kirjoittamalla \"{{ viesti }}\". Iteroidaksesi kaikki lomakkeen kentät läpi " 205 | "voit käyttää \"data\"-nimistä muuttujaa (lista joka sisältää dictin jossa on elementit \"name\", " 206 | "\"label\" ja \"value\" per lomakekenttä)." 207 | 208 | #: .\models.py:51 209 | msgid "form template" 210 | msgstr "lomakepohja" 211 | 212 | #: .\models.py:52 213 | msgid "display logged submissions with form" 214 | msgstr "näytä tämän lomakkeen lokissa olevat lähetykset" 215 | 216 | #: .\models.py:56 217 | msgid "Forms" 218 | msgstr "Lomakkeet" 219 | 220 | #: .\models.py:116 221 | msgid "Fields" 222 | msgstr "Kentät" 223 | 224 | #: .\models.py:177 225 | msgid "field class" 226 | msgstr "kentän tyyppi" 227 | 228 | #: .\models.py:178 229 | msgid "position" 230 | msgstr "sijainti" 231 | 232 | #: .\models.py:181 233 | msgid "label" 234 | msgstr "otsikko" 235 | 236 | #: .\models.py:182 237 | msgid "required" 238 | msgstr "pakollinen" 239 | 240 | #: .\models.py:183 241 | msgid "include in result" 242 | msgstr "sisällytä tulokseen" 243 | 244 | #: .\models.py:184 245 | msgid "widget" 246 | msgstr "vimpain" 247 | 248 | #: .\models.py:185 249 | msgid "initial value" 250 | msgstr "oletusarvo" 251 | 252 | #: .\models.py:186 253 | msgid "help text" 254 | msgstr "ohjeteksti" 255 | 256 | #: .\models.py:188 257 | msgid "values" 258 | msgstr "arvot" 259 | 260 | #: .\models.py:188 261 | msgid "One value per line" 262 | msgstr "Yksi arvo per rivi" 263 | 264 | #: .\models.py:189 265 | msgid "labels" 266 | msgstr "otsikot" 267 | 268 | #: .\models.py:189 269 | msgid "One label per line" 270 | msgstr "Yksi otsikko per rivi" 271 | 272 | #: .\models.py:191 273 | msgid "max. length" 274 | msgstr "enimmäispituus" 275 | 276 | #: .\models.py:192 277 | msgid "min. length" 278 | msgstr "vähimmäispituus" 279 | 280 | #: .\models.py:193 281 | msgid "max. value" 282 | msgstr "enimmäisarvo" 283 | 284 | #: .\models.py:194 285 | msgid "min. value" 286 | msgstr "vähimmäisarvo" 287 | 288 | #: .\models.py:195 289 | msgid "max. digits" 290 | msgstr "numeroiden enimmäismäärä" 291 | 292 | #: .\models.py:196 293 | msgid "decimal places" 294 | msgstr "desimaalitarkkuus" 295 | 296 | #: .\models.py:198 297 | msgid "regular Expression" 298 | msgstr "säännöllinen lauseke" 299 | 300 | #: .\models.py:201 301 | msgid "data model" 302 | msgstr "tietokantamalli" 303 | 304 | #: .\models.py:202 305 | msgid "empty label" 306 | msgstr "tyhjä otsikko" 307 | 308 | #: .\models.py:205 309 | msgid "field" 310 | msgstr "kenttä" 311 | 312 | #: .\models.py:206 313 | msgid "fields" 314 | msgstr "kentät" 315 | 316 | #: .\models.py:297 .\contrib\exporters\__init__.py:60 317 | msgid "Created" 318 | msgstr "Luotu" 319 | 320 | #: .\models.py:363 321 | msgid "field name" 322 | msgstr "kentän nimi" 323 | 324 | #: .\models.py:367 .\models.py:372 325 | msgid "value" 326 | msgstr "arvo" 327 | 328 | #: .\settings.py:11 329 | msgid "E-mail address" 330 | msgstr "Sähköpostiosoite" 331 | 332 | #: .\settings.py:12 333 | msgid "Web address" 334 | msgstr "Www-osoite" 335 | 336 | #: .\settings.py:13 337 | msgid "Number" 338 | msgstr "Luku" 339 | 340 | #: .\settings.py:14 341 | msgid "Decimal number" 342 | msgstr "Desimaaliluku" 343 | 344 | #: .\settings.py:15 345 | msgid "Yes/No" 346 | msgstr "Kyllä/Ei" 347 | 348 | #: .\settings.py:16 349 | msgid "Date" 350 | msgstr "Päivämäärä" 351 | 352 | #: .\settings.py:17 353 | msgid "Date & time" 354 | msgstr "Päivämäärä ja aika" 355 | 356 | #: .\settings.py:18 357 | msgid "Time" 358 | msgstr "Aika" 359 | 360 | #: .\settings.py:19 361 | msgid "Choice" 362 | msgstr "Valinta" 363 | 364 | #: .\settings.py:20 365 | msgid "Multiple Choice" 366 | msgstr "Monivalinta" 367 | 368 | #: .\settings.py:21 369 | msgid "Model Choice" 370 | msgstr "Tietokantamallivalinta" 371 | 372 | #: .\settings.py:22 373 | msgid "Model Multiple Choice" 374 | msgstr "Monivalinta (tietokannasta)" 375 | 376 | #: .\settings.py:24 377 | msgid "File" 378 | msgstr "Tiedosto" 379 | 380 | #: .\settings.py:29 .\settings.py:42 381 | msgid "Default" 382 | msgstr "Oletus" 383 | 384 | #: .\settings.py:30 385 | msgid "Text area" 386 | msgstr "Tekstikenttä" 387 | 388 | #: .\settings.py:31 389 | msgid "Password input" 390 | msgstr "Salasanakenttä" 391 | 392 | #: .\settings.py:32 393 | msgid "Hidden input" 394 | msgstr "Piilotettu kenttä" 395 | 396 | #: .\settings.py:33 397 | msgid "Radio button" 398 | msgstr "Valintanappi (radio)" 399 | 400 | #: .\settings.py:43 401 | msgid "as paragraphs" 402 | msgstr "tekstikappaleina" 403 | 404 | #: .\settings.py:44 405 | msgid "as table" 406 | msgstr "taulukkona" 407 | 408 | #: .\settings.py:45 409 | msgid "as table (horizontal)" 410 | msgstr "taulukkona (vaakasuuntaisesti)" 411 | 412 | #: .\settings.py:46 413 | msgid "as unordered list" 414 | msgstr "listana" 415 | 416 | #: .\settings.py:47 417 | msgid "custom implementation" 418 | msgstr "räätälöity ratkaisu" 419 | 420 | #: .\uploads.py:20 421 | msgid "This field is required." 422 | msgstr "Tämä kenttä on pakollinen." 423 | 424 | #: .\uploads.py:25 425 | msgid "This file type is not allowed." 426 | msgstr "Tämä tiedostomuoto on kielletty." 427 | 428 | #: .\uploads.py:27 429 | #, python-format 430 | msgid "Please keep file size under %(max_size)s. Current size is %(size)s." 431 | msgstr "Tiedostojen enimmäiskoko on %(max_size)s. Nykyinen koko on %(size)s." 432 | 433 | #: .\views.py:21 434 | msgid "Thank you, the data was submitted successfully." 435 | msgstr "Kiitos! Lomake lähetetty." 436 | 437 | #: .\views.py:22 438 | msgid "The data could not be submitted, please try again." 439 | msgstr "Lähetys epäonnistui, ole hyvä ja kokeile uudestaan." 440 | 441 | #: .\contrib\cms_plugins\form_designer_form\cms_plugins.py:13 442 | msgid "Form Designer" 443 | msgstr "Lomaketyökalu" 444 | 445 | #: .\contrib\cms_plugins\form_designer_form\models.py:8 446 | msgid "form" 447 | msgstr "lomake" 448 | 449 | #: .\contrib\exporters\__init__.py:62 450 | msgid "ID" 451 | msgstr "ID" 452 | 453 | #: .\templates\html\formdefinition\forms\includes\submit.html.py:2 454 | msgid "Submit" 455 | msgstr "Lähetä" 456 | 457 | #: .\templatetags\friendly.py:20 458 | msgid "yes" 459 | msgstr "kyllä" 460 | 461 | #: .\templatetags\friendly.py:20 462 | msgid "no" 463 | msgstr "ei" 464 | 465 | #: .\views.py:50 466 | msgid "Invalid reCAPTCHA." 467 | msgstr "Virheellinen reCAPTCHA." 468 | -------------------------------------------------------------------------------- /form_designer/locale/fr/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andersinno/django-form-designer-ai/104f205035681e0743372f6f22de96bc6eca9d6c/form_designer/locale/fr/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /form_designer/locale/fr/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # django-form-designer locale. 2 | # This file is distributed under the same license as the django-form-designer package. 3 | # SIMON CHARETTE , 2011. 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: 1\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "POT-Creation-Date: 2012-01-19 20:56-0500\n" 10 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 11 | "Last-Translator: SIMON CHARETTE \n" 12 | "Language-Team: fr\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: 8bit\n" 16 | "Language: \n" 17 | 18 | #: admin.py:27 admin.py:38 19 | msgid "Basic" 20 | msgstr "Base" 21 | 22 | #: admin.py:28 23 | msgid "Display" 24 | msgstr "Affichage" 25 | 26 | #: admin.py:29 settings.py:10 27 | msgid "Text" 28 | msgstr "Texte" 29 | 30 | #: admin.py:30 31 | msgid "Numbers" 32 | msgstr "Nombres" 33 | 34 | #: admin.py:31 settings.py:23 35 | msgid "Regex" 36 | msgstr "Expression régulière" 37 | 38 | #: admin.py:32 39 | msgid "Choices" 40 | msgstr "Choix" 41 | 42 | #: admin.py:33 43 | msgid "Model Choices" 44 | msgstr "Choix de Modèles" 45 | 46 | #: admin.py:39 47 | msgid "Settings" 48 | msgstr "Réglages" 49 | 50 | #: admin.py:40 51 | msgid "Mail form" 52 | msgstr "Envoi de courriels" 53 | 54 | #: admin.py:41 55 | msgid "Templates" 56 | msgstr "Gabarits" 57 | 58 | #: admin.py:42 59 | msgid "Messages" 60 | msgstr "Messages" 61 | 62 | #: admin.py:63 admin.py:102 admin.py:147 models.py:60 models.py:173 63 | #: contrib/cms_plugins/form_designer_form/cms_plugins.py:14 64 | msgid "Form" 65 | msgstr "Formulaire" 66 | 67 | #: admin.py:78 models.py:174 68 | msgid "Data" 69 | msgstr "Données" 70 | 71 | #: admin.py:104 admin.py:149 models.py:172 72 | msgid "Created" 73 | msgstr "Créé" 74 | 75 | #: admin.py:106 admin.py:151 76 | msgid "ID" 77 | msgstr "ID" 78 | 79 | #: admin.py:125 80 | #, python-format 81 | msgid "Export selected %(verbose_name_plural)s as CSV" 82 | msgstr "Exporter les %(verbose_name_plural)s en CSV" 83 | 84 | #: admin.py:173 85 | #, python-format 86 | msgid "Export selected %(verbose_name_plural)s as XLS" 87 | msgstr "Exporter les %(verbose_name_plural)s en XLS" 88 | 89 | #: admin.py:185 90 | msgid "CSV export is not enabled." 91 | msgstr "L'exportation en CSV n'est pas activé." 92 | 93 | #: admin.py:190 94 | msgid "XLS export is not enabled." 95 | msgstr "L'exportation en XLS n'est pas activé" 96 | 97 | #: forms.py:43 98 | msgid "This field class requires a regular expression." 99 | msgstr "Ce type de champ necessite une expression régulière." 100 | 101 | #: forms.py:48 102 | msgid "This field class requires a model." 103 | msgstr "Cette classe de champ nécessite un modèle." 104 | 105 | #: models.py:35 models.py:187 106 | msgid "name" 107 | msgstr "Nom" 108 | 109 | #: models.py:36 110 | msgid "obfuscate URL to this form" 111 | msgstr "Obscurcir l'URL vers ce formulaire" 112 | 113 | #: models.py:36 114 | msgid "If enabled, the form can only be reached via a secret URL." 115 | msgstr "" 116 | "Si activé, il sera seulement possible d'atteindre le formulaire via une URL " 117 | "secrète." 118 | 119 | #: models.py:39 120 | msgid "title" 121 | msgstr "Titre" 122 | 123 | #: models.py:40 124 | msgid "body" 125 | msgstr "Corps" 126 | 127 | #: models.py:41 128 | msgid "target URL" 129 | msgstr "URL cible" 130 | 131 | #: models.py:41 132 | msgid "" 133 | "If you leave this empty, the page where the form resides will be requested, " 134 | "and you can use the mail form and logging features. You can also send data " 135 | "to external sites: For instance, enter \"http://www.google.ch/search\" to " 136 | "create a search form." 137 | msgstr "" 138 | "Si vous laisser ce champs vide, le formulaire sera envoyé vers la page où il " 139 | "se trouve et vous pourrez utiliser les fonctionnalités de courriel et de " 140 | "sauvegarde. Vous pouvez aussi envoyer les données saisies vers un site " 141 | "externe: Par exemple, entrez \"http://www.google.fr/search\" pour créer un " 142 | "formulaire de recherche." 143 | 144 | #: models.py:42 145 | msgid "send form data to e-mail address" 146 | msgstr "Envoyer les données saisies aux adresses courriels." 147 | 148 | #: models.py:43 149 | msgid "sender address" 150 | msgstr "Addresse de l'expéditeur" 151 | 152 | #: models.py:44 153 | msgid "email subject" 154 | msgstr "Sujet du courriel" 155 | 156 | #: models.py:45 157 | msgid "Send uploaded files as email attachments" 158 | msgstr "Joindre les fichiers téléversés aux courriels." 159 | 160 | #: models.py:46 161 | msgid "method" 162 | msgstr "Méthode" 163 | 164 | #: models.py:47 165 | msgid "success message" 166 | msgstr "Message de succès" 167 | 168 | #: models.py:48 169 | msgid "error message" 170 | msgstr "Message d'erreur" 171 | 172 | #: models.py:49 173 | msgid "submit button label" 174 | msgstr "Libellé du bouton d'envoi" 175 | 176 | #: models.py:50 177 | msgid "log form data" 178 | msgstr "Sauver les données" 179 | 180 | #: models.py:50 181 | msgid "Logs all form submissions to the database." 182 | msgstr "Sauver les données saisies dans la base de données." 183 | 184 | #: models.py:51 185 | msgid "save uploaded files" 186 | msgstr "Sauver les fichiers téléversés" 187 | 188 | #: models.py:51 189 | msgid "Saves all uploaded files using server storage." 190 | msgstr "" 191 | "Sauve tous les fichiers téléversés en utilisant la mécanique de stockage du " 192 | "serveur." 193 | 194 | #: models.py:52 195 | msgid "HTTP redirect after successful submission" 196 | msgstr "Redirection HTTP après une soumission réussie " 197 | 198 | #: models.py:53 199 | msgid "clear form after successful submission" 200 | msgstr "Vider les champs après une soumission réussie" 201 | 202 | #: models.py:54 203 | msgid "allow initial values via URL" 204 | msgstr "Permettre l'assignation de valeurs via l'URL" 205 | 206 | #: models.py:54 207 | msgid "" 208 | "If enabled, you can fill in form fields by adding them to the query string." 209 | msgstr "" 210 | "Si activé, vous pouvez populer les champs du formulaire en les ajoutants à " 211 | "la chaîne de requête: Par exemple \"http://chemin.vers/mon/formulaire?" 212 | "prenom=Serge" 213 | 214 | #: models.py:55 215 | msgid "message template" 216 | msgstr "Gabarit de message de courriel" 217 | 218 | #: models.py:55 219 | msgid "" 220 | "Your form fields are available as template context. Example: \"{{ message }}" 221 | "\" if you have a field named `message`. To iterate over all fields, use the " 222 | "variable `data` (a list containing a dictionary for each form field, each " 223 | "containing the elements `name`, `label`, `value`)." 224 | msgstr "" 225 | "Vos champs de formulaire sont accessibles sous forme de variables de " 226 | "gabarits.Par exemple: \"{{ message }}\" si vous avez un champ nommé «" 227 | " message ».Pour itérer sur l'ensemble des champs, " 228 | "utilisez la variables « data » ( une liste " 229 | "de dictionnaires associé à chaque champ avec les clefs « " 230 | "name », « label », « " 231 | "value » )" 232 | 233 | #: models.py:56 234 | msgid "form template" 235 | msgstr "Gabarit de formulaire" 236 | 237 | #: models.py:57 238 | msgid "display logged submissions with form" 239 | msgstr "Afficher les données soumises avec le formulaire" 240 | 241 | #: models.py:61 242 | msgid "Forms" 243 | msgstr "Formulaires" 244 | 245 | #: models.py:116 246 | msgid "Fields" 247 | msgstr "Champs" 248 | 249 | #: models.py:177 250 | msgid "Form log" 251 | msgstr "Entrée de formulaire" 252 | 253 | #: models.py:178 254 | msgid "Form logs" 255 | msgstr "Entrées de formulaire" 256 | 257 | #: models.py:184 258 | msgid "field class" 259 | msgstr "Type de champ" 260 | 261 | #: models.py:185 262 | msgid "position" 263 | msgstr "Position" 264 | 265 | #: models.py:188 266 | msgid "label" 267 | msgstr "Libellé" 268 | 269 | #: models.py:189 270 | msgid "required" 271 | msgstr "Obligatoire" 272 | 273 | #: models.py:190 274 | msgid "include in result" 275 | msgstr "Inclure dans le résultat" 276 | 277 | #: models.py:191 278 | msgid "widget" 279 | msgstr "Widget" 280 | 281 | #: models.py:192 282 | msgid "initial value" 283 | msgstr "Valeur initiale" 284 | 285 | #: models.py:193 286 | msgid "help text" 287 | msgstr "Texte d'aide" 288 | 289 | #: models.py:195 290 | msgid "values" 291 | msgstr "Valeurs" 292 | 293 | #: models.py:195 294 | msgid "One value per line" 295 | msgstr "Une valeur par ligne" 296 | 297 | #: models.py:196 298 | msgid "labels" 299 | msgstr "Libellés" 300 | 301 | #: models.py:196 302 | msgid "One label per line" 303 | msgstr "Un libellé par ligne" 304 | 305 | #: models.py:198 306 | msgid "max. length" 307 | msgstr "Longueur maximale" 308 | 309 | #: models.py:199 310 | msgid "min. length" 311 | msgstr "Longueur minimale" 312 | 313 | #: models.py:200 314 | msgid "max. value" 315 | msgstr "Valeur maximale" 316 | 317 | #: models.py:201 318 | msgid "min. value" 319 | msgstr "Valeur minimale" 320 | 321 | #: models.py:202 322 | msgid "max. digits" 323 | msgstr "Nombre de chiffres maximum" 324 | 325 | #: models.py:203 326 | msgid "decimal places" 327 | msgstr "Nombre de décimales" 328 | 329 | #: models.py:205 330 | msgid "regular Expression" 331 | msgstr "Expression régulière" 332 | 333 | #: models.py:208 334 | msgid "data model" 335 | msgstr "Modèle de données" 336 | 337 | #: models.py:209 338 | msgid "empty label" 339 | msgstr "Libellé vide" 340 | 341 | #: models.py:212 342 | msgid "field" 343 | msgstr "Champ" 344 | 345 | #: models.py:213 346 | msgid "fields" 347 | msgstr "Champs" 348 | 349 | #: settings.py:11 350 | msgid "E-mail address" 351 | msgstr "Adresse courriel" 352 | 353 | #: settings.py:12 354 | msgid "Web address" 355 | msgstr "Adresse Web" 356 | 357 | #: settings.py:13 358 | msgid "Number" 359 | msgstr "Nombre" 360 | 361 | #: settings.py:14 362 | msgid "Decimal number" 363 | msgstr "Nombre décimal" 364 | 365 | #: settings.py:15 366 | msgid "Yes/No" 367 | msgstr "Oui/Non" 368 | 369 | #: settings.py:16 370 | msgid "Date" 371 | msgstr "Date" 372 | 373 | #: settings.py:17 374 | msgid "Date & time" 375 | msgstr "Date et temps" 376 | 377 | #: settings.py:18 378 | msgid "Time" 379 | msgstr "Temps" 380 | 381 | #: settings.py:19 382 | msgid "Choice" 383 | msgstr "Choix" 384 | 385 | #: settings.py:20 386 | msgid "Multiple Choice" 387 | msgstr "Choix Multiple" 388 | 389 | #: settings.py:21 390 | msgid "Model Choice" 391 | msgstr "Choix de Modèle" 392 | 393 | #: settings.py:22 394 | msgid "Model Multiple Choice" 395 | msgstr "Choix Multiple de Modèle" 396 | 397 | #: settings.py:24 398 | msgid "File" 399 | msgstr "Fichier" 400 | 401 | #: settings.py:29 settings.py:37 402 | msgid "Default" 403 | msgstr "Défaut" 404 | 405 | #: settings.py:30 406 | msgid "Text area" 407 | msgstr "Zone de texte" 408 | 409 | #: settings.py:31 410 | msgid "Password input" 411 | msgstr "Champ de saisie de mot de passe" 412 | 413 | #: settings.py:32 414 | msgid "Hidden input" 415 | msgstr "Champ caché" 416 | 417 | #: settings.py:33 418 | msgid "Radio button" 419 | msgstr "Bouton radio" 420 | 421 | #: settings.py:38 422 | msgid "as paragraphs" 423 | msgstr "en paragraphes" 424 | 425 | #: settings.py:39 426 | msgid "as table" 427 | msgstr "en table" 428 | 429 | #: settings.py:40 430 | msgid "as table (horizontal)" 431 | msgstr "en table (horizontale)" 432 | 433 | #: settings.py:41 434 | msgid "as unordered list" 435 | msgstr "en liste non ordonnée (ul)" 436 | 437 | #: settings.py:42 438 | msgid "custom implementation" 439 | msgstr "implémentation personnalisée" 440 | 441 | #: uploads.py:20 442 | msgid "This field is required." 443 | msgstr "Ce champs est obligatoire." 444 | 445 | #: uploads.py:25 446 | msgid "This file type is not allowed." 447 | msgstr "Ce type de fichier n'est pas autorisé." 448 | 449 | #: uploads.py:27 450 | #, python-format 451 | msgid "Please keep file size under %(max_size)s. Current size is %(size)s." 452 | msgstr "" 453 | "Veuillez garder une taille de ficher inférieure à %(max_size)s. " 454 | "La taille actuelle est de %(size)s." 455 | 456 | #: views.py:21 457 | msgid "Thank you, the data was submitted successfully." 458 | msgstr "Merci, les données ont été soumises avec succès." 459 | 460 | #: views.py:22 461 | msgid "The data could not be submitted, please try again." 462 | msgstr "Les données n'ont pas pu être soumises, veuillez essayer de nouveau." 463 | 464 | #: contrib/cms_plugins/form_designer_form/cms_plugins.py:13 465 | msgid "Form Designer" 466 | msgstr "" 467 | 468 | #: contrib/cms_plugins/form_designer_form/models.py:8 469 | msgid "form" 470 | msgstr "Formulaire" 471 | 472 | #: templates/admin/form_designer/formlog/change_list.html:9 473 | msgid "Export CSV" 474 | msgstr "Exporter en CSV" 475 | 476 | #: templates/admin/form_designer/formlog/change_list.html:16 477 | msgid "Export XLS" 478 | msgstr "Exporter en XML" 479 | 480 | #: templates/html/formdefinition/forms/includes/submit.html:2 481 | msgid "Submit" 482 | msgstr "Envoyer" 483 | 484 | #: templatetags/friendly.py:18 485 | msgid "yes" 486 | msgstr "oui" 487 | 488 | #: templatetags/friendly.py:18 489 | msgid "no" 490 | msgstr "non" 491 | -------------------------------------------------------------------------------- /form_designer/locale/nl/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andersinno/django-form-designer-ai/104f205035681e0743372f6f22de96bc6eca9d6c/form_designer/locale/nl/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /form_designer/locale/nl/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: PACKAGE VERSION\n" 4 | "Report-Msgid-Bugs-To: \n" 5 | "POT-Creation-Date: 2013-10-29 18:36+0100\n" 6 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 7 | "Last-Translator: Erik Romijn \n" 8 | "Language-Team: LANGUAGE \n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | 13 | #: admin.py:18 admin.py:30 14 | msgid "Basic" 15 | msgstr "Basis" 16 | 17 | #: admin.py:19 18 | msgid "Display" 19 | msgstr "Weergave" 20 | 21 | #: admin.py:20 settings.py:10 22 | msgid "Text" 23 | msgstr "Tekst" 24 | 25 | #: admin.py:21 26 | msgid "Numbers" 27 | msgstr "Nummers" 28 | 29 | #: admin.py:22 settings.py:23 30 | msgid "Regex" 31 | msgstr "Reguliere expressie" 32 | 33 | #: admin.py:23 34 | msgid "Choices" 35 | msgstr "Keuzes" 36 | 37 | #: admin.py:24 38 | msgid "Model Choices" 39 | msgstr "Model-keuzes" 40 | 41 | #: admin.py:31 42 | msgid "Settings" 43 | msgstr "" 44 | 45 | #: admin.py:32 46 | msgid "Mail form" 47 | msgstr "Mail-formulier" 48 | 49 | #: admin.py:33 50 | msgid "Templates" 51 | msgstr "Sjablonen" 52 | 53 | #: admin.py:34 54 | msgid "Messages" 55 | msgstr "Berichten" 56 | 57 | #: admin.py:63 58 | #, fuzzy, python-format 59 | msgid "Export selected %%(verbose_name_plural)s as %s" 60 | msgstr "Exporteer geselecteerde %(verbose_name_plural) als CSV" 61 | 62 | #: admin.py:73 models.py:62 63 | #: contrib/cms_plugins/form_designer_form/cms_plugins.py:14 64 | #: contrib/exporters/__init__.py:58 65 | msgid "Form" 66 | msgstr "Formulier" 67 | 68 | #: admin.py:84 69 | msgid "Data" 70 | msgstr "Data" 71 | 72 | #: admin.py:108 73 | #, python-format 74 | msgid "Export view as %s" 75 | msgstr "Exporteer view als %s" 76 | 77 | #: forms.py:44 78 | msgid "This field class requires a regular expression." 79 | msgstr "Deze veldklasse vereist een reguliere expressie." 80 | 81 | #: forms.py:49 82 | msgid "This field class requires a model." 83 | msgstr "Deze veldklasse vereist een model." 84 | 85 | #: models.py:37 models.py:187 86 | msgid "name" 87 | msgstr "Naam" 88 | 89 | #: models.py:38 90 | msgid "obfuscate URL to this form" 91 | msgstr "verberg URL naar dit formulier" 92 | 93 | #: models.py:38 94 | msgid "If enabled, the form can only be reached via a secret URL." 95 | msgstr "" 96 | "Indien actief, kan het formulier enkel bezocht worden via een geheime URL." 97 | 98 | #: models.py:41 99 | msgid "title" 100 | msgstr "Titel" 101 | 102 | #: models.py:42 103 | msgid "body" 104 | msgstr "" 105 | 106 | #: models.py:43 107 | msgid "target URL" 108 | msgstr "Doel-URL" 109 | 110 | #: models.py:43 111 | msgid "" 112 | "If you leave this empty, the page where the form resides will be requested, " 113 | "and you can use the mail form and logging features. You can also send data " 114 | "to external sites: For instance, enter \"http://www.google.ch/search\" to " 115 | "create a search form." 116 | msgstr "" 117 | "Als u dit leeg laat, wordt de pagina waar het formulier zich bevindt " 118 | "opgevraagd en kan u de e-mail en log-functies gebruiken. U kunt ook data " 119 | "naar externe sites versturen. Bijvoorbeeld, voer \"http://www.google.com/" 120 | "search\" in om een zoekformulier aan te maken. " 121 | 122 | #: models.py:44 123 | msgid "send form data to e-mail address" 124 | msgstr "Formuliergegevens naar e-mail-adres sturen" 125 | 126 | #: models.py:45 127 | msgid "sender address" 128 | msgstr "Afzender-adres" 129 | 130 | #: models.py:46 131 | msgid "email subject" 132 | msgstr "E-mail-onderwerp" 133 | 134 | #: models.py:47 135 | msgid "Send uploaded files as email attachments" 136 | msgstr "Verstuur geüploade bestanden als mailbijlages" 137 | 138 | #: models.py:48 139 | msgid "method" 140 | msgstr "Methode" 141 | 142 | #: models.py:49 143 | msgid "success message" 144 | msgstr "Successmelding" 145 | 146 | #: models.py:50 147 | msgid "error message" 148 | msgstr "Foutmelding" 149 | 150 | #: models.py:51 151 | msgid "submit button label" 152 | msgstr "Label voor de verzendknop" 153 | 154 | #: models.py:52 155 | msgid "log form data" 156 | msgstr "Formulierdata loggen" 157 | 158 | #: models.py:52 159 | msgid "Logs all form submissions to the database." 160 | msgstr "Log alle formulierinsturingen in de database." 161 | 162 | #: models.py:53 163 | msgid "save uploaded files" 164 | msgstr "bewaar geüploade bestanden" 165 | 166 | #: models.py:53 167 | msgid "Saves all uploaded files using server storage." 168 | msgstr "Bewaart alle geüploade bestanden op de server." 169 | 170 | #: models.py:54 171 | msgid "HTTP redirect after successful submission" 172 | msgstr "Doorsturen na success" 173 | 174 | #: models.py:55 175 | msgid "clear form after successful submission" 176 | msgstr "Na succesvol versturen formulier leegmaken" 177 | 178 | #: models.py:56 179 | msgid "allow initial values via URL" 180 | msgstr "Initiële waarden via de URL toelaten" 181 | 182 | #: models.py:56 183 | msgid "" 184 | "If enabled, you can fill in form fields by adding them to the query string." 185 | msgstr "" 186 | "Indien geactiveerd, kunt u formuliervelden invullen door deze aan de query-" 187 | "string toe te voegen. " 188 | 189 | #: models.py:57 190 | msgid "message template" 191 | msgstr "Berichtsjabloon" 192 | 193 | #: models.py:57 194 | msgid "" 195 | "Your form fields are available as template context. Example: \"{{ message }}" 196 | "\" if you have a field named `message`. To iterate over all fields, use the " 197 | "variable `data` (a list containing a dictionary for each form field, each " 198 | "containing the elements `name`, `label`, `value`)." 199 | msgstr "" 200 | "Uw formuliervelden zijn beschikbaar als template context. Bijvoorbeeld: " 201 | "\"{{ bericht }}\" als u een veld genaamd `bericht` heeft. Om over alle " 202 | "velden te itereren, gebruikt u de variabele `data`: een lijst met een dict " 203 | "voor elk veld met de elementen `name`, `label` en `value`." 204 | 205 | #: models.py:58 206 | msgid "form template" 207 | msgstr "Formulier-sjabloon" 208 | 209 | #: models.py:59 210 | msgid "display logged submissions with form" 211 | msgstr "toon gelogde inzendingen met dit formulier" 212 | 213 | #: models.py:63 214 | msgid "Forms" 215 | msgstr "Formulieren" 216 | 217 | #: models.py:123 218 | msgid "Fields" 219 | msgstr "Velden" 220 | 221 | #: models.py:184 222 | msgid "field class" 223 | msgstr "Veldklasse" 224 | 225 | #: models.py:185 226 | msgid "position" 227 | msgstr "Positie" 228 | 229 | #: models.py:188 230 | msgid "label" 231 | msgstr "Label" 232 | 233 | #: models.py:189 234 | msgid "required" 235 | msgstr "Vereist" 236 | 237 | #: models.py:190 238 | msgid "include in result" 239 | msgstr "In resultaat plaatsen" 240 | 241 | #: models.py:191 242 | msgid "widget" 243 | msgstr "Widget" 244 | 245 | #: models.py:192 246 | msgid "initial value" 247 | msgstr "Initiële waarde" 248 | 249 | #: models.py:193 250 | msgid "help text" 251 | msgstr "Hulptekst" 252 | 253 | #: models.py:195 254 | msgid "values" 255 | msgstr "Waardes" 256 | 257 | #: models.py:195 258 | msgid "One value per line" 259 | msgstr "Eén waarde per regel" 260 | 261 | #: models.py:196 262 | msgid "labels" 263 | msgstr "Labels" 264 | 265 | #: models.py:196 266 | msgid "One label per line" 267 | msgstr "Eén label per regel" 268 | 269 | #: models.py:198 270 | msgid "max. length" 271 | msgstr "Max. lengte" 272 | 273 | #: models.py:199 274 | msgid "min. length" 275 | msgstr "Min. lengte" 276 | 277 | #: models.py:200 278 | msgid "max. value" 279 | msgstr "Max. waarde" 280 | 281 | #: models.py:201 282 | msgid "min. value" 283 | msgstr "Min. waarde" 284 | 285 | #: models.py:202 286 | msgid "max. digits" 287 | msgstr "Max. aantal cijfers" 288 | 289 | #: models.py:203 290 | msgid "decimal places" 291 | msgstr "Decimalen" 292 | 293 | #: models.py:205 294 | msgid "regular Expression" 295 | msgstr "Reguliere expressie" 296 | 297 | #: models.py:208 298 | msgid "data model" 299 | msgstr "Datamodel" 300 | 301 | #: models.py:209 302 | msgid "empty label" 303 | msgstr "Leeg label" 304 | 305 | #: models.py:212 306 | msgid "field" 307 | msgstr "Veld" 308 | 309 | #: models.py:213 310 | msgid "fields" 311 | msgstr "Velden" 312 | 313 | #: models.py:304 contrib/exporters/__init__.py:60 314 | msgid "Created" 315 | msgstr "Aangemaakt" 316 | 317 | #: models.py:370 318 | #, fuzzy 319 | msgid "field name" 320 | msgstr "Veldklasse" 321 | 322 | #: models.py:374 models.py:379 323 | #, fuzzy 324 | msgid "value" 325 | msgstr "Waardes" 326 | 327 | #: settings.py:11 328 | msgid "E-mail address" 329 | msgstr "E-mail-adres" 330 | 331 | #: settings.py:12 332 | msgid "Web address" 333 | msgstr "Web-adres" 334 | 335 | #: settings.py:13 336 | msgid "Number" 337 | msgstr "Nummer" 338 | 339 | #: settings.py:14 340 | msgid "Decimal number" 341 | msgstr "Decimaal nummer" 342 | 343 | #: settings.py:15 344 | msgid "Yes/No" 345 | msgstr "Ja/Nee" 346 | 347 | #: settings.py:16 348 | msgid "Date" 349 | msgstr "Datum" 350 | 351 | #: settings.py:17 352 | msgid "Date & time" 353 | msgstr "Datum & tijd" 354 | 355 | #: settings.py:18 356 | msgid "Time" 357 | msgstr "Tijd" 358 | 359 | #: settings.py:19 360 | msgid "Choice" 361 | msgstr "Keuze" 362 | 363 | #: settings.py:20 364 | msgid "Multiple Choice" 365 | msgstr "Meerkeuze" 366 | 367 | #: settings.py:21 368 | msgid "Model Choice" 369 | msgstr "Model-keuze" 370 | 371 | #: settings.py:22 372 | msgid "Model Multiple Choice" 373 | msgstr "Model-meerkeuze" 374 | 375 | #: settings.py:24 376 | msgid "File" 377 | msgstr "Veld" 378 | 379 | #: settings.py:29 settings.py:42 380 | msgid "Default" 381 | msgstr "Standaard" 382 | 383 | #: settings.py:30 384 | msgid "Text area" 385 | msgstr "Tekstveld" 386 | 387 | #: settings.py:31 388 | msgid "Password input" 389 | msgstr "Wachtwoordveld" 390 | 391 | #: settings.py:32 392 | msgid "Hidden input" 393 | msgstr "Verborgen veld" 394 | 395 | #: settings.py:33 396 | msgid "Radio button" 397 | msgstr "Keuzerondje" 398 | 399 | #: settings.py:43 400 | msgid "as paragraphs" 401 | msgstr "als paragraaf" 402 | 403 | #: settings.py:44 404 | msgid "as table" 405 | msgstr "als tabel" 406 | 407 | #: settings.py:45 408 | msgid "as table (horizontal)" 409 | msgstr "als tabel (horizontaal)" 410 | 411 | #: settings.py:46 412 | msgid "as unordered list" 413 | msgstr "als ongesorteerde lijst" 414 | 415 | #: settings.py:47 416 | msgid "custom implementation" 417 | msgstr "eigen implementatie" 418 | 419 | #: uploads.py:20 420 | msgid "This field is required." 421 | msgstr "Dit veld is verplicht." 422 | 423 | #: uploads.py:25 424 | msgid "This file type is not allowed." 425 | msgstr "Dit type veld is niet toegestaan." 426 | 427 | #: uploads.py:27 428 | #, python-format 429 | msgid "Please keep file size under %(max_size)s. Current size is %(size)s." 430 | msgstr "" 431 | "Beperk de bestandsgrootte tot maximum %(max_size)s. Huidige bestandsgrootte " 432 | "is %(size)s." 433 | 434 | #: views.py:21 435 | msgid "Thank you, the data was submitted successfully." 436 | msgstr "Hartelijk dank. Uw gegevens zijn doorgestuurd." 437 | 438 | #: views.py:22 439 | msgid "The data could not be submitted, please try again." 440 | msgstr "De gegevens konden niet opgestuurd worden, probeert u het opnieuw." 441 | 442 | #: contrib/cms_plugins/form_designer_form/cms_plugins.py:13 443 | msgid "Form Designer" 444 | msgstr "" 445 | 446 | #: contrib/cms_plugins/form_designer_form/models.py:8 447 | msgid "form" 448 | msgstr "Formulier" 449 | 450 | #: contrib/exporters/__init__.py:62 451 | msgid "ID" 452 | msgstr "ID" 453 | 454 | #: templates/html/formdefinition/forms/includes/submit.html:2 455 | msgid "Submit" 456 | msgstr "Verzenden" 457 | 458 | #: templatetags/friendly.py:20 459 | msgid "yes" 460 | msgstr "ja" 461 | 462 | #: templatetags/friendly.py:20 463 | msgid "no" 464 | msgstr "nee" 465 | 466 | #, fuzzy 467 | #~ msgid "Export selected %(verbose_name_plural)s as XLS" 468 | #~ msgstr "Exporteer geselecteerde %(verbose_name_plural) als Excel XLS" 469 | 470 | #~ msgid "CSV export is not enabled." 471 | #~ msgstr "CSV-exporteren is niet geäctiveerd." 472 | 473 | #~ msgid "XLS export is not enabled." 474 | #~ msgstr "XLS-exporteren is niet geäctiveerd." 475 | 476 | #~ msgid "Form log" 477 | #~ msgstr "Formulier-log" 478 | 479 | #~ msgid "Form logs" 480 | #~ msgstr "Formulier-logs" 481 | 482 | #~ msgid "Export CSV" 483 | #~ msgstr "CSV exporteren" 484 | 485 | #~ msgid "Export XLS" 486 | #~ msgstr "XLS exporteren" 487 | -------------------------------------------------------------------------------- /form_designer/locale/pt_BR/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andersinno/django-form-designer-ai/104f205035681e0743372f6f22de96bc6eca9d6c/form_designer/locale/pt_BR/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /form_designer/locale/pt_BR/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 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2013-02-14 16:13-0200\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \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:18 admin.py:30 22 | msgid "Basic" 23 | msgstr "Básico" 24 | 25 | #: admin.py:19 26 | msgid "Display" 27 | msgstr "" 28 | 29 | #: admin.py:20 settings.py:10 30 | msgid "Text" 31 | msgstr "Texto" 32 | 33 | #: admin.py:21 34 | msgid "Numbers" 35 | msgstr "Números" 36 | 37 | #: admin.py:22 settings.py:23 38 | msgid "Regex" 39 | msgstr "Expressão Regular" 40 | 41 | #: admin.py:23 42 | msgid "Choices" 43 | msgstr "Escolhas" 44 | 45 | #: admin.py:24 46 | msgid "Model Choices" 47 | msgstr "" 48 | 49 | #: admin.py:31 50 | msgid "Settings" 51 | msgstr "Configurações" 52 | 53 | #: admin.py:32 54 | msgid "Mail form" 55 | msgstr "Formulário de Email" 56 | 57 | #: admin.py:33 58 | msgid "Templates" 59 | msgstr "" 60 | 61 | #: admin.py:34 62 | msgid "Messages" 63 | msgstr "Mensagens" 64 | 65 | #: admin.py:63 66 | #, python-format 67 | msgid "Export selected %%(verbose_name_plural)s as %s" 68 | msgstr "Exportar %%(verbose_name_plural)s selecionado como %s" 69 | 70 | #: admin.py:73 models.py:55 71 | #: contrib/cms_plugins/form_designer_form/cms_plugins.py:14 72 | #: contrib/exporters/__init__.py:58 73 | msgid "Form" 74 | msgstr "Formulário" 75 | 76 | #: admin.py:84 77 | msgid "Data" 78 | msgstr "Dados" 79 | 80 | #: admin.py:108 81 | #, python-format 82 | msgid "Export view as %s" 83 | msgstr "Exportar view como %s" 84 | 85 | #: forms.py:44 86 | msgid "This field class requires a regular expression." 87 | msgstr "Esta classe de campo requer uma expressão regular" 88 | 89 | #: forms.py:49 90 | msgid "This field class requires a model." 91 | msgstr "Esta classe de campo requer um model" 92 | 93 | #: models.py:30 models.py:180 94 | msgid "name" 95 | msgstr "nome" 96 | 97 | #: models.py:31 98 | msgid "obfuscate URL to this form" 99 | msgstr "ofuscar URL para esse form" 100 | 101 | #: models.py:31 102 | msgid "If enabled, the form can only be reached via a secret URL." 103 | msgstr "Se habilitado, o formulário só poderá ser alcançado via uma URL secreta." 104 | 105 | #: models.py:34 106 | msgid "title" 107 | msgstr "título" 108 | 109 | #: models.py:35 110 | msgid "body" 111 | msgstr "corpo" 112 | 113 | #: models.py:36 114 | msgid "target URL" 115 | msgstr "URL alvo" 116 | 117 | #: models.py:36 118 | msgid "" 119 | "If you leave this empty, the page where the form resides will be requested, " 120 | "and you can use the mail form and logging features. You can also send data " 121 | "to external sites: For instance, enter \"http://www.google.ch/search\" to " 122 | "create a search form." 123 | msgstr "" 124 | "Se você o deixar vazio, a página onde o formulário reside será requisitada, " 125 | "e você pode usar o formulário de email e ferramentas de registro. Você pode também enviar dados" 126 | "para sites externos: Por exemplo, coloque \"http://www.google.com.br/search\" para " 127 | "crear um formulário de pesquisa." 128 | 129 | 130 | #: models.py:37 131 | msgid "send form data to e-mail address" 132 | msgstr "enviar os dados do formulário para o endereço de email" 133 | 134 | #: models.py:38 135 | msgid "sender address" 136 | msgstr "endereço do remetente" 137 | 138 | #: models.py:39 139 | msgid "email subject" 140 | msgstr "assunto do email" 141 | 142 | #: models.py:40 143 | msgid "Send uploaded files as email attachments" 144 | msgstr "Enviar arquivos como anexos do email" 145 | 146 | #: models.py:41 147 | msgid "method" 148 | msgstr "método" 149 | 150 | #: models.py:42 151 | msgid "success message" 152 | msgstr "mensagem de sucesso" 153 | 154 | #: models.py:43 155 | msgid "error message" 156 | msgstr "mensagem de erro" 157 | 158 | #: models.py:44 159 | msgid "submit button label" 160 | msgstr "texto do botão de submit" 161 | 162 | #: models.py:45 163 | msgid "log form data" 164 | msgstr "registrar dados do formulário" 165 | 166 | #: models.py:45 167 | msgid "Logs all form submissions to the database." 168 | msgstr "Registrar no banco de dados todos os envios de formulário." 169 | 170 | #: models.py:46 171 | msgid "save uploaded files" 172 | msgstr "salvar arquivos enviados" 173 | 174 | #: models.py:46 175 | msgid "Saves all uploaded files using server storage." 176 | msgstr "Salvar todos os arquivos enviados usando um server storage." 177 | 178 | #: models.py:47 179 | msgid "HTTP redirect after successful submission" 180 | msgstr "Executar um HTTP redirect depois do envio com sucesso de um formulário" 181 | 182 | #: models.py:48 183 | msgid "clear form after successful submission" 184 | msgstr "limpar o formulário depois de um envio de sucesso" 185 | 186 | #: models.py:49 187 | msgid "allow initial values via URL" 188 | msgstr "permitir valores iniciais via URL" 189 | 190 | #: models.py:49 191 | msgid "" 192 | "If enabled, you can fill in form fields by adding them to the query string." 193 | msgstr "Se habilitado, você pode preencher os campos do formulário adicionando seus valores à query string" 194 | 195 | #: models.py:50 196 | msgid "message template" 197 | msgstr "template de mensagem" 198 | 199 | #: models.py:50 200 | msgid "" 201 | "Your form fields are available as template context. Example: " 202 | "\"{{ message }}\" if you have a field named `message`. To iterate over all " 203 | "fields, use the variable `data` (a list containing a dictionary for each " 204 | "form field, each containing the elements `name`, `label`, `value`)." 205 | msgstr "" 206 | "Seus campos de formulário são disponíveis no contexto da template. Exemplo:" 207 | "\"{{ message }}\" se você possui um campo nomeado `message`. Para iterar sobre todos " 208 | "os campos, use a variável `data` (uma lista contendo um dicionário para cada " 209 | "campo de formulário, os quais contém, por sua vez, os elementos `name`, `label`, " 210 | "`value`)." 211 | 212 | #: models.py:51 213 | msgid "form template" 214 | msgstr "template de formulário" 215 | 216 | #: models.py:52 217 | msgid "display logged submissions with form" 218 | msgstr "mostrar registros de envio com o form" 219 | 220 | #: models.py:56 221 | msgid "Forms" 222 | msgstr "Formulários" 223 | 224 | #: models.py:116 225 | msgid "Fields" 226 | msgstr "Campos" 227 | 228 | #: models.py:177 229 | msgid "field class" 230 | msgstr "classe do campo" 231 | 232 | #: models.py:178 233 | msgid "position" 234 | msgstr "posição" 235 | 236 | #: models.py:181 237 | msgid "label" 238 | msgstr "" 239 | 240 | #: models.py:182 241 | msgid "required" 242 | msgstr "obrigatório" 243 | 244 | #: models.py:183 245 | msgid "include in result" 246 | msgstr "incluído no resultado" 247 | 248 | #: models.py:184 249 | msgid "widget" 250 | msgstr "" 251 | 252 | #: models.py:185 253 | msgid "initial value" 254 | msgstr "valor inicial" 255 | 256 | #: models.py:186 257 | msgid "help text" 258 | msgstr "texto de ajuda" 259 | 260 | #: models.py:188 261 | msgid "values" 262 | msgstr "valores" 263 | 264 | #: models.py:188 265 | msgid "One value per line" 266 | msgstr "Um valor por linha" 267 | 268 | #: models.py:189 269 | msgid "labels" 270 | msgstr "" 271 | 272 | #: models.py:189 273 | msgid "One label per line" 274 | msgstr "Um label por linha" 275 | 276 | #: models.py:191 277 | msgid "max. length" 278 | msgstr "valor máximo" 279 | 280 | #: models.py:192 281 | msgid "min. length" 282 | msgstr "valor mínimo" 283 | 284 | #: models.py:193 285 | msgid "max. value" 286 | msgstr "valor máximo" 287 | 288 | #: models.py:194 289 | msgid "min. value" 290 | msgstr "valor mínimo" 291 | 292 | #: models.py:195 293 | msgid "max. digits" 294 | msgstr "máximo de dígitos" 295 | 296 | #: models.py:196 297 | msgid "decimal places" 298 | msgstr "casas decimais" 299 | 300 | #: models.py:198 301 | msgid "regular Expression" 302 | msgstr "expressão regular" 303 | 304 | #: models.py:201 305 | msgid "data model" 306 | msgstr "modelo de dados" 307 | 308 | #: models.py:202 309 | msgid "empty label" 310 | msgstr "label vazio" 311 | 312 | #: models.py:205 313 | msgid "field" 314 | msgstr "campo" 315 | 316 | #: models.py:206 317 | msgid "fields" 318 | msgstr "campos" 319 | 320 | #: models.py:297 contrib/exporters/__init__.py:60 321 | msgid "Created" 322 | msgstr "Criado" 323 | 324 | #: models.py:363 325 | msgid "field name" 326 | msgstr "nome do campo" 327 | 328 | #: models.py:367 models.py:372 329 | msgid "value" 330 | msgstr "valor" 331 | 332 | #: settings.py:11 333 | msgid "E-mail address" 334 | msgstr "Endereço de email" 335 | 336 | #: settings.py:12 337 | msgid "Web address" 338 | msgstr "Endereço Web" 339 | 340 | #: settings.py:13 341 | msgid "Number" 342 | msgstr "Número" 343 | 344 | #: settings.py:14 345 | msgid "Decimal number" 346 | msgstr "Número Decimal" 347 | 348 | #: settings.py:15 349 | msgid "Yes/No" 350 | msgstr "Sim/Não" 351 | 352 | #: settings.py:16 353 | msgid "Date" 354 | msgstr "Data" 355 | 356 | #: settings.py:17 357 | msgid "Date & time" 358 | msgstr "Data & hora" 359 | 360 | #: settings.py:18 361 | msgid "Time" 362 | msgstr "Hora" 363 | 364 | #: settings.py:19 365 | msgid "Choice" 366 | msgstr "Escolha" 367 | 368 | #: settings.py:20 369 | msgid "Multiple Choice" 370 | msgstr "Escolha Múltipla" 371 | 372 | #: settings.py:21 373 | msgid "Model Choice" 374 | msgstr "" 375 | 376 | #: settings.py:22 377 | msgid "Model Multiple Choice" 378 | msgstr "" 379 | 380 | #: settings.py:24 381 | msgid "File" 382 | msgstr "Arquivo" 383 | 384 | #: settings.py:29 settings.py:42 385 | msgid "Default" 386 | msgstr "" 387 | 388 | #: settings.py:30 389 | msgid "Text area" 390 | msgstr "" 391 | 392 | #: settings.py:31 393 | msgid "Password input" 394 | msgstr "" 395 | 396 | #: settings.py:32 397 | msgid "Hidden input" 398 | msgstr "" 399 | 400 | #: settings.py:33 401 | msgid "Radio button" 402 | msgstr "" 403 | 404 | #: settings.py:43 405 | msgid "as paragraphs" 406 | msgstr "como parágrafos" 407 | 408 | #: settings.py:44 409 | msgid "as table" 410 | msgstr "como tabela" 411 | 412 | #: settings.py:45 413 | msgid "as table (horizontal)" 414 | msgstr "como tabela (horizontal)" 415 | 416 | #: settings.py:46 417 | msgid "as unordered list" 418 | msgstr "como lista não ordenada" 419 | 420 | #: settings.py:47 421 | msgid "custom implementation" 422 | msgstr "implementação customizada" 423 | 424 | #: uploads.py:20 425 | msgid "This field is required." 426 | msgstr "Este campo é obrigatório" 427 | 428 | #: uploads.py:25 429 | msgid "This file type is not allowed." 430 | msgstr "Este tipo de arquivo não é permitido" 431 | 432 | #: uploads.py:27 433 | #, python-format 434 | msgid "Please keep file size under %(max_size)s. Current size is %(size)s." 435 | msgstr "" 436 | "Por favor, informe um arquivo menor que %(max_size)s. O tamanho do arquivo " 437 | "enviado é %(size)s." 438 | 439 | #: views.py:21 440 | msgid "Thank you, the data was submitted successfully." 441 | msgstr "Obrigado, os dados foram enviados com sucesso." 442 | 443 | #: views.py:22 444 | msgid "The data could not be submitted, please try again." 445 | msgstr "Os dados não puderam ser enviados, por favor, tente novamente." 446 | 447 | #: contrib/cms_plugins/form_designer_form/cms_plugins.py:13 448 | msgid "Form Designer" 449 | msgstr "" 450 | 451 | #: contrib/cms_plugins/form_designer_form/models.py:8 452 | msgid "form" 453 | msgstr "formulário" 454 | 455 | #: contrib/exporters/__init__.py:62 456 | msgid "ID" 457 | msgstr "" 458 | 459 | #: templates/html/formdefinition/forms/includes/submit.html:2 460 | msgid "Submit" 461 | msgstr "Enviar" 462 | 463 | #: templatetags/friendly.py:20 464 | msgid "yes" 465 | msgstr "sim" 466 | 467 | #: templatetags/friendly.py:20 468 | msgid "no" 469 | msgstr "não" 470 | -------------------------------------------------------------------------------- /form_designer/locale/ru/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andersinno/django-form-designer-ai/104f205035681e0743372f6f22de96bc6eca9d6c/form_designer/locale/ru/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /form_designer/locale/sv/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andersinno/django-form-designer-ai/104f205035681e0743372f6f22de96bc6eca9d6c/form_designer/locale/sv/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /form_designer/locale/sv/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | 4 | #: admin.py:18 admin.py:30 5 | msgid "Basic" 6 | msgstr "Bas" 7 | 8 | #: admin.py:19 9 | msgid "Display" 10 | msgstr "Visning" 11 | 12 | #: admin.py:20 settings.py:10 13 | msgid "Text" 14 | msgstr "Text" 15 | 16 | #: admin.py:21 17 | msgid "Numbers" 18 | msgstr "Siffror" 19 | 20 | #: admin.py:22 settings.py:23 21 | msgid "Regex" 22 | msgstr "Regex" 23 | 24 | #: admin.py:23 25 | msgid "Choices" 26 | msgstr "Val" 27 | 28 | #: admin.py:24 29 | msgid "Model Choices" 30 | msgstr "Objektval" 31 | 32 | #: admin.py:31 33 | msgid "Settings" 34 | msgstr "Inställningar" 35 | 36 | #: admin.py:32 37 | msgid "Mail form" 38 | msgstr "E-postformulär" 39 | 40 | #: admin.py:33 41 | msgid "Templates" 42 | msgstr "Mallar" 43 | 44 | #: admin.py:34 45 | msgid "Messages" 46 | msgstr "Meddelanden" 47 | 48 | #: admin.py:63 49 | #, python-format 50 | msgid "Export selected %%(verbose_name_plural)s as %s" 51 | msgstr "Exportera valt %%(verbose_name_plural)s som %s" 52 | 53 | #: admin.py:73 models.py:55 contrib/exporters/__init__.py:58 54 | msgid "Form" 55 | msgstr "Blankett" 56 | 57 | #: admin.py:84 58 | msgid "Data" 59 | msgstr "Data" 60 | 61 | #: admin.py:108 62 | #, python-format 63 | msgid "Export view as %s" 64 | msgstr "Exportera vy som %s" 65 | 66 | #: forms.py:44 67 | msgid "This field class requires a regular expression." 68 | msgstr "Detta fältklass kräver ett regex." 69 | 70 | #: forms.py:49 71 | msgid "This field class requires a model." 72 | msgstr "Detta fältklass kräver en modell." 73 | 74 | #: models.py:30 models.py:182 75 | msgid "name" 76 | msgstr "namn" 77 | 78 | #: models.py:31 79 | msgid "obfuscate URL to this form" 80 | msgstr "fördunkla URL till denna form" 81 | 82 | #: models.py:31 83 | msgid "If enabled, the form can only be reached via a secret URL." 84 | msgstr "Om aktiverad, kan formuläret endast nås via en hemlig webbadress." 85 | 86 | #: models.py:34 87 | msgid "title" 88 | msgstr "titel" 89 | 90 | #: models.py:35 91 | msgid "body" 92 | msgstr "kropp" 93 | 94 | #: models.py:36 95 | msgid "target URL" 96 | msgstr "mål-URL" 97 | 98 | #: models.py:36 99 | msgid "" 100 | "If you leave this empty, the page where the form resides will be " 101 | "requested, and you can use the mail form and logging features. You can " 102 | "also send data to external sites: For instance, enter " 103 | "\"http://www.google.ch/search\" to create a search form." 104 | msgstr "Om du lämnar detta tomt, sidan där formulär bosatt kommer att begäras, och kan du använda postformuläret och loggningfunktioner. Du kan också skicka data till externa webbplatser: Till exempel, skriv in http://www.google.ch/search för att skapa ett sökformulär." 105 | 106 | #: models.py:37 107 | msgid "send form data to e-mail address" 108 | msgstr "skicka formulärdata till e-postadress" 109 | 110 | #: models.py:38 111 | msgid "sender address" 112 | msgstr "avsändaradress" 113 | 114 | #: models.py:39 115 | msgid "email subject" 116 | msgstr "Epostämne" 117 | 118 | #: models.py:40 119 | msgid "Send uploaded files as email attachments" 120 | msgstr "Skicka uppladdade filer som bilagor" 121 | 122 | #: models.py:41 123 | msgid "method" 124 | msgstr "metod" 125 | 126 | #: models.py:42 127 | msgid "success message" 128 | msgstr "framgångmeddelande" 129 | 130 | #: models.py:43 131 | msgid "error message" 132 | msgstr "felmeddelande" 133 | 134 | #: models.py:44 135 | msgid "submit button label" 136 | msgstr "etikett för skicka-knappen" 137 | 138 | #: models.py:45 139 | msgid "log form data" 140 | msgstr "log blankettdata" 141 | 142 | #: models.py:45 143 | msgid "Logs all form submissions to the database." 144 | msgstr "Loggar alla blankettinlagor till databasen." 145 | 146 | #: models.py:46 147 | msgid "save uploaded files" 148 | msgstr "spara uppladdade filer" 149 | 150 | #: models.py:46 151 | msgid "Saves all uploaded files using server storage." 152 | msgstr "Sparar alla uppladdade filer med serverlagring." 153 | 154 | #: models.py:47 155 | msgid "HTTP redirect after successful submission" 156 | msgstr "HTTP-omdirigering efter lyckad inlämning" 157 | 158 | #: models.py:48 159 | msgid "clear form after successful submission" 160 | msgstr "putsa form efter lyckad inlämning" 161 | 162 | #: models.py:49 163 | msgid "allow initial values via URL" 164 | msgstr "tillåta initialvärden via webbadress" 165 | 166 | #: models.py:49 167 | msgid "" 168 | "If enabled, you can fill in form fields by adding them to the query " 169 | "string." 170 | msgstr "Om det är aktiverat, kan du fylla i formulärfält genom att lägga till dem i frågesträngen." 171 | 172 | #: models.py:50 173 | msgid "message template" 174 | msgstr "meddelandemall" 175 | 176 | #: models.py:50 177 | msgid "" 178 | "Your form fields are available as template context. Example: \"{{ message" 179 | " }}\" if you have a field named `message`. To iterate over all fields, " 180 | "use the variable `data` (a list containing a dictionary for each form " 181 | "field, each containing the elements `name`, `label`, `value`)." 182 | msgstr "Formulärfält finns som mallsammanhang. Exempel: "{{meddelande}}" om du har ett fält som heter `meddelande`. För att iterera över alla fält, använda variabeln `data` (en lista som innehåller en ordlista för varje fält, var och en innehåller den element `name`, `label`, `value`)." 183 | 184 | #: models.py:51 185 | msgid "form template" 186 | msgstr "formulärmall" 187 | 188 | #: models.py:52 189 | msgid "display logged submissions with form" 190 | msgstr "visa loggade inlagor med formulär" 191 | 192 | #: models.py:56 193 | msgid "Forms" 194 | msgstr "Blanketter" 195 | 196 | #: models.py:116 197 | msgid "Fields" 198 | msgstr "Fält" 199 | 200 | #: models.py:179 201 | msgid "field class" 202 | msgstr "fältklass" 203 | 204 | #: models.py:180 205 | msgid "position" 206 | msgstr "ställning" 207 | 208 | #: models.py:183 209 | msgid "label" 210 | msgstr "etikett" 211 | 212 | #: models.py:184 213 | msgid "required" 214 | msgstr "krävs" 215 | 216 | #: models.py:185 217 | msgid "include in result" 218 | msgstr "inkludera i resultat" 219 | 220 | #: models.py:186 221 | msgid "widget" 222 | msgstr "widget" 223 | 224 | #: models.py:187 225 | msgid "initial value" 226 | msgstr "initialvärde" 227 | 228 | #: models.py:188 229 | msgid "help text" 230 | msgstr "hjälptext" 231 | 232 | #: models.py:190 233 | msgid "values" 234 | msgstr "värden" 235 | 236 | #: models.py:190 237 | msgid "One value per line" 238 | msgstr "Ett värde per rad" 239 | 240 | #: models.py:191 241 | msgid "labels" 242 | msgstr "etiketter" 243 | 244 | #: models.py:191 245 | msgid "One label per line" 246 | msgstr "En etikett per rad" 247 | 248 | #: models.py:193 249 | msgid "max. length" 250 | msgstr "max. längd" 251 | 252 | #: models.py:194 253 | msgid "min. length" 254 | msgstr "min. längd" 255 | 256 | #: models.py:195 257 | msgid "max. value" 258 | msgstr "max. värde" 259 | 260 | #: models.py:196 261 | msgid "min. value" 262 | msgstr "min. värde" 263 | 264 | #: models.py:197 265 | msgid "max. digits" 266 | msgstr "max. siffror" 267 | 268 | #: models.py:198 269 | msgid "decimal places" 270 | msgstr "decimaler" 271 | 272 | #: models.py:200 273 | msgid "regular Expression" 274 | msgstr "regex" 275 | 276 | #: models.py:203 277 | msgid "data model" 278 | msgstr "datamodell" 279 | 280 | #: models.py:204 281 | msgid "empty label" 282 | msgstr "tom etikett" 283 | 284 | #: models.py:207 285 | msgid "field" 286 | msgstr "fält" 287 | 288 | #: models.py:208 289 | msgid "fields" 290 | msgstr "fälter" 291 | 292 | #: models.py:299 contrib/exporters/__init__.py:60 293 | msgid "Created" 294 | msgstr "Skapat" 295 | 296 | #: models.py:365 297 | msgid "field name" 298 | msgstr "fältnamn" 299 | 300 | #: models.py:369 models.py:374 301 | msgid "value" 302 | msgstr "värde" 303 | 304 | #: settings.py:11 305 | msgid "E-mail address" 306 | msgstr "E-postadress" 307 | 308 | #: settings.py:12 309 | msgid "Web address" 310 | msgstr "Webbadress" 311 | 312 | #: settings.py:13 313 | msgid "Number" 314 | msgstr "Nummer" 315 | 316 | #: settings.py:14 317 | msgid "Decimal number" 318 | msgstr "Decimalnummer" 319 | 320 | #: settings.py:15 321 | msgid "Yes/No" 322 | msgstr "Ja / Nej" 323 | 324 | #: settings.py:16 325 | msgid "Date" 326 | msgstr "Datum" 327 | 328 | #: settings.py:17 329 | msgid "Date & time" 330 | msgstr "Datum och tid" 331 | 332 | #: settings.py:18 333 | msgid "Time" 334 | msgstr "Tid" 335 | 336 | #: settings.py:19 337 | msgid "Choice" 338 | msgstr "Väljfält" 339 | 340 | #: settings.py:20 341 | msgid "Multiple Choice" 342 | msgstr "Flervalsfråga" 343 | 344 | #: settings.py:21 345 | msgid "Model Choice" 346 | msgstr "Objektväljfält" 347 | 348 | #: settings.py:22 349 | msgid "Model Multiple Choice" 350 | msgstr "Objektflervalsfråga" 351 | 352 | #: settings.py:24 353 | msgid "File" 354 | msgstr "Fil" 355 | 356 | #: settings.py:29 settings.py:42 357 | msgid "Default" 358 | msgstr "Förval" 359 | 360 | #: settings.py:30 361 | msgid "Text area" 362 | msgstr "Textruta" 363 | 364 | #: settings.py:31 365 | msgid "Password input" 366 | msgstr "Lösenordruta" 367 | 368 | #: settings.py:32 369 | msgid "Hidden input" 370 | msgstr "Dold" 371 | 372 | #: settings.py:33 373 | msgid "Radio button" 374 | msgstr "Radio-knapp" 375 | 376 | #: settings.py:43 377 | msgid "as paragraphs" 378 | msgstr "som paragrafer" 379 | 380 | #: settings.py:44 381 | msgid "as table" 382 | msgstr "som tabell" 383 | 384 | #: settings.py:45 385 | msgid "as table (horizontal)" 386 | msgstr "som tabell (horisontellt) 387 | " 388 | 389 | #: settings.py:46 390 | msgid "as unordered list" 391 | msgstr "som oordnad lista" 392 | 393 | #: settings.py:47 394 | msgid "custom implementation" 395 | msgstr "anpassad implementering" 396 | 397 | #: uploads.py:20 398 | msgid "This field is required." 399 | msgstr "Detta fält är obligatoriskt." 400 | 401 | #: uploads.py:25 402 | msgid "This file type is not allowed." 403 | msgstr "Den här filtypen är inte tillåtet." 404 | 405 | #: uploads.py:27 406 | #, python-format 407 | msgid "Please keep file size under %(max_size)s. Current size is %(size)s." 408 | msgstr "Vänligen håll filstorleken i %(max_size)s. Nuvarande storlek är %(size)s." 409 | 410 | #: views.py:21 411 | msgid "Thank you, the data was submitted successfully." 412 | msgstr "Tack, data har skickats." 413 | 414 | #: views.py:22 415 | msgid "The data could not be submitted, please try again." 416 | msgstr "Uppgifterna kan inte sparas, vänligen försök igen." 417 | 418 | #: contrib/exporters/__init__.py:62 419 | msgid "ID" 420 | msgstr "ID" 421 | 422 | #: templatetags/friendly.py:20 423 | msgid "yes" 424 | msgstr "ja" 425 | 426 | #: templatetags/friendly.py:20 427 | msgid "no" 428 | msgstr "nej" 429 | 430 | #~ msgid "Export selected %(verbose_name_plural)s as CSV" 431 | #~ msgstr "" 432 | 433 | #~ msgid "Export selected %(verbose_name_plural)s as XLS" 434 | #~ msgstr "" 435 | 436 | #~ msgid "CSV export is not enabled." 437 | #~ msgstr "" 438 | 439 | #~ msgid "XLS export is not enabled." 440 | #~ msgstr "" 441 | 442 | #~ msgid "Form log" 443 | #~ msgstr "" 444 | 445 | #~ msgid "Form logs" 446 | #~ msgstr "" 447 | 448 | #~ msgid "Form Designer" 449 | #~ msgstr "" 450 | 451 | #~ msgid "form" 452 | #~ msgstr "" 453 | 454 | #~ msgid "Export CSV" 455 | #~ msgstr "" 456 | 457 | #~ msgid "Export XLS" 458 | #~ msgstr "" 459 | 460 | #~ msgid "Submit" 461 | #~ msgstr "" 462 | 463 | -------------------------------------------------------------------------------- /form_designer/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 1.9.6 on 2016-05-19 07:05 2 | 3 | import django.db.models.deletion 4 | import picklefield.fields 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | 8 | import form_designer.fields 9 | 10 | 11 | class Migration(migrations.Migration): 12 | initial = True 13 | 14 | dependencies = [ 15 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 16 | ] 17 | 18 | operations = [ 19 | migrations.CreateModel( 20 | name="FormDefinition", 21 | fields=[ 22 | ( 23 | "id", 24 | models.AutoField( 25 | auto_created=True, 26 | primary_key=True, 27 | serialize=False, 28 | verbose_name="ID", 29 | ), 30 | ), 31 | ( 32 | "name", 33 | models.SlugField(max_length=255, unique=True, verbose_name="name"), 34 | ), 35 | ( 36 | "require_hash", 37 | models.BooleanField( 38 | default=False, 39 | help_text="If enabled, the form can only be reached via a secret URL.", 40 | verbose_name="obfuscate URL to this form", 41 | ), 42 | ), 43 | ( 44 | "private_hash", 45 | models.CharField(default="", editable=False, max_length=40), 46 | ), 47 | ( 48 | "public_hash", 49 | models.CharField(default="", editable=False, max_length=40), 50 | ), 51 | ( 52 | "title", 53 | models.CharField( 54 | blank=True, max_length=255, null=True, verbose_name="title" 55 | ), 56 | ), 57 | ( 58 | "body", 59 | models.TextField( 60 | blank=True, 61 | null=True, 62 | verbose_name="body", 63 | help_text="Form description. Display on form after title.", 64 | ), 65 | ), 66 | ( 67 | "action", 68 | models.URLField( 69 | blank=True, 70 | help_text='If you leave this empty, the page where the form resides will be requested, and you can use the mail form and logging features. You can also send data to external sites: For instance, enter "http://www.google.ch/search" to create a search form.', 71 | max_length=255, 72 | null=True, 73 | verbose_name="target URL", 74 | ), 75 | ), 76 | ( 77 | "mail_to", 78 | form_designer.fields.TemplateCharField( 79 | blank=True, 80 | help_text='Separate several addresses with a comma. Your form fields are available as template context. Example: "admin@domain.com, {{ from_email }}" if you have a field named `from_email`.', 81 | max_length=255, 82 | null=True, 83 | verbose_name="send form data to e-mail address", 84 | ), 85 | ), 86 | ( 87 | "mail_from", 88 | form_designer.fields.TemplateCharField( 89 | blank=True, 90 | help_text='Your form fields are available as template context. Example: "{{ first_name }} {{ last_name }} <{{ from_email }}>" if you have fields named `first_name`, `last_name`, `from_email`.', 91 | max_length=255, 92 | null=True, 93 | verbose_name="sender address", 94 | ), 95 | ), 96 | ( 97 | "mail_subject", 98 | form_designer.fields.TemplateCharField( 99 | blank=True, 100 | help_text='Your form fields are available as template context. Example: "Contact form {{ subject }}" if you have a field named `subject`.', 101 | max_length=255, 102 | null=True, 103 | verbose_name="email subject", 104 | ), 105 | ), 106 | ( 107 | "mail_uploaded_files", 108 | models.BooleanField( 109 | default=True, 110 | verbose_name="Send uploaded files as email attachments", 111 | ), 112 | ), 113 | ( 114 | "method", 115 | models.CharField( 116 | choices=[("POST", "POST"), ("GET", "GET")], 117 | default="POST", 118 | max_length=10, 119 | verbose_name="method", 120 | ), 121 | ), 122 | ( 123 | "success_message", 124 | models.CharField( 125 | blank=True, 126 | max_length=255, 127 | null=True, 128 | verbose_name="success message", 129 | ), 130 | ), 131 | ( 132 | "error_message", 133 | models.CharField( 134 | blank=True, 135 | max_length=255, 136 | null=True, 137 | verbose_name="error message", 138 | ), 139 | ), 140 | ( 141 | "submit_label", 142 | models.CharField( 143 | blank=True, 144 | max_length=255, 145 | null=True, 146 | verbose_name="submit button label", 147 | ), 148 | ), 149 | ( 150 | "log_data", 151 | models.BooleanField( 152 | default=True, 153 | help_text="Logs all form submissions to the database.", 154 | verbose_name="log form data", 155 | ), 156 | ), 157 | ( 158 | "save_uploaded_files", 159 | models.BooleanField( 160 | default=True, 161 | help_text="Saves all uploaded files using server storage.", 162 | verbose_name="save uploaded files", 163 | ), 164 | ), 165 | ( 166 | "success_redirect", 167 | models.BooleanField( 168 | default=True, 169 | verbose_name="HTTP redirect after successful submission", 170 | ), 171 | ), 172 | ( 173 | "success_clear", 174 | models.BooleanField( 175 | default=True, 176 | verbose_name="clear form after successful submission", 177 | ), 178 | ), 179 | ( 180 | "allow_get_initial", 181 | models.BooleanField( 182 | default=True, 183 | help_text="If enabled, you can fill in form fields by adding them to the query string.", 184 | verbose_name="allow initial values via URL", 185 | ), 186 | ), 187 | ( 188 | "message_template", 189 | form_designer.fields.TemplateTextField( 190 | blank=True, 191 | help_text='Your form fields are available as template context. Example: "{{ message }}" if you have a field named `message`. To iterate over all fields, use the variable `data` (a list containing a dictionary for each form field, each containing the elements `name`, `label`, `value`).', 192 | null=True, 193 | verbose_name="message template", 194 | ), 195 | ), 196 | ( 197 | "form_template_name", 198 | models.CharField( 199 | blank=True, 200 | max_length=255, 201 | null=True, 202 | verbose_name="form template", 203 | ), 204 | ), 205 | ( 206 | "display_logged", 207 | models.BooleanField( 208 | default=False, 209 | verbose_name="display logged submissions with form", 210 | ), 211 | ), 212 | ], 213 | options={ 214 | "verbose_name": "Form", 215 | "verbose_name_plural": "Forms", 216 | }, 217 | ), 218 | migrations.CreateModel( 219 | name="FormDefinitionField", 220 | fields=[ 221 | ( 222 | "id", 223 | models.AutoField( 224 | auto_created=True, 225 | primary_key=True, 226 | serialize=False, 227 | verbose_name="ID", 228 | ), 229 | ), 230 | ( 231 | "field_class", 232 | models.CharField(max_length=100, verbose_name="field class"), 233 | ), 234 | ( 235 | "position", 236 | models.IntegerField(blank=True, null=True, verbose_name="position"), 237 | ), 238 | ("name", models.SlugField(max_length=255, verbose_name="name")), 239 | ( 240 | "label", 241 | models.CharField( 242 | blank=True, max_length=255, null=True, verbose_name="label" 243 | ), 244 | ), 245 | ( 246 | "required", 247 | models.BooleanField(default=True, verbose_name="required"), 248 | ), 249 | ( 250 | "include_result", 251 | models.BooleanField( 252 | default=True, 253 | help_text="If this is disabled, the field value will not be included in logs and e-mails generated from form data.", 254 | verbose_name="include in result", 255 | ), 256 | ), 257 | ( 258 | "widget", 259 | models.CharField( 260 | blank=True, 261 | default="", 262 | max_length=255, 263 | null=True, 264 | verbose_name="widget", 265 | ), 266 | ), 267 | ( 268 | "initial", 269 | models.TextField( 270 | blank=True, null=True, verbose_name="initial value" 271 | ), 272 | ), 273 | ( 274 | "help_text", 275 | models.CharField( 276 | blank=True, max_length=255, null=True, verbose_name="help text" 277 | ), 278 | ), 279 | ( 280 | "choice_values", 281 | models.TextField( 282 | blank=True, 283 | help_text="One value per line", 284 | null=True, 285 | verbose_name="values", 286 | ), 287 | ), 288 | ( 289 | "choice_labels", 290 | models.TextField( 291 | blank=True, 292 | help_text="One label per line", 293 | null=True, 294 | verbose_name="labels", 295 | ), 296 | ), 297 | ( 298 | "max_length", 299 | models.IntegerField( 300 | blank=True, null=True, verbose_name="max. length" 301 | ), 302 | ), 303 | ( 304 | "min_length", 305 | models.IntegerField( 306 | blank=True, null=True, verbose_name="min. length" 307 | ), 308 | ), 309 | ( 310 | "max_value", 311 | models.FloatField(blank=True, null=True, verbose_name="max. value"), 312 | ), 313 | ( 314 | "min_value", 315 | models.FloatField(blank=True, null=True, verbose_name="min. value"), 316 | ), 317 | ( 318 | "max_digits", 319 | models.IntegerField( 320 | blank=True, null=True, verbose_name="max. digits" 321 | ), 322 | ), 323 | ( 324 | "decimal_places", 325 | models.IntegerField( 326 | blank=True, null=True, verbose_name="decimal places" 327 | ), 328 | ), 329 | ( 330 | "regex", 331 | form_designer.fields.RegexpExpressionField( 332 | blank=True, 333 | max_length=255, 334 | null=True, 335 | verbose_name="regular Expression", 336 | ), 337 | ), 338 | ( 339 | "choice_model", 340 | form_designer.fields.ModelNameField( 341 | blank=True, max_length=255, null=True, verbose_name="data model" 342 | ), 343 | ), 344 | ( 345 | "choice_model_empty_label", 346 | models.CharField( 347 | blank=True, 348 | max_length=255, 349 | null=True, 350 | verbose_name="empty label", 351 | ), 352 | ), 353 | ( 354 | "form_definition", 355 | models.ForeignKey( 356 | on_delete=django.db.models.deletion.CASCADE, 357 | to="form_designer.FormDefinition", 358 | ), 359 | ), 360 | ], 361 | options={ 362 | "verbose_name": "field", 363 | "verbose_name_plural": "fields", 364 | "ordering": ["position"], 365 | }, 366 | ), 367 | migrations.CreateModel( 368 | name="FormLog", 369 | fields=[ 370 | ( 371 | "id", 372 | models.AutoField( 373 | auto_created=True, 374 | primary_key=True, 375 | serialize=False, 376 | verbose_name="ID", 377 | ), 378 | ), 379 | ( 380 | "created", 381 | models.DateTimeField(auto_now=True, verbose_name="Created"), 382 | ), 383 | ( 384 | "created_by", 385 | models.ForeignKey( 386 | blank=True, 387 | null=True, 388 | on_delete=django.db.models.deletion.CASCADE, 389 | to=settings.AUTH_USER_MODEL, 390 | ), 391 | ), 392 | ( 393 | "form_definition", 394 | models.ForeignKey( 395 | on_delete=django.db.models.deletion.CASCADE, 396 | related_name="logs", 397 | to="form_designer.FormDefinition", 398 | ), 399 | ), 400 | ], 401 | options={ 402 | "verbose_name": "form log", 403 | "verbose_name_plural": "form logs", 404 | }, 405 | ), 406 | migrations.CreateModel( 407 | name="FormValue", 408 | fields=[ 409 | ( 410 | "id", 411 | models.AutoField( 412 | auto_created=True, 413 | primary_key=True, 414 | serialize=False, 415 | verbose_name="ID", 416 | ), 417 | ), 418 | ( 419 | "field_name", 420 | models.SlugField(max_length=255, verbose_name="field name"), 421 | ), 422 | ( 423 | "value", 424 | picklefield.fields.PickledObjectField( 425 | blank=True, editable=False, null=True, verbose_name="value" 426 | ), 427 | ), 428 | ( 429 | "form_log", 430 | models.ForeignKey( 431 | on_delete=django.db.models.deletion.CASCADE, 432 | related_name="values", 433 | to="form_designer.FormLog", 434 | ), 435 | ), 436 | ], 437 | ), 438 | ] 439 | -------------------------------------------------------------------------------- /form_designer/migrations/0002_reply_to.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 1.9.7 on 2016-06-06 06:50 2 | 3 | from django.db import migrations 4 | 5 | import form_designer.fields 6 | 7 | 8 | class Migration(migrations.Migration): 9 | dependencies = [ 10 | ("form_designer", "0001_initial"), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name="formdefinition", 16 | name="mail_reply_to", 17 | field=form_designer.fields.TemplateCharField( 18 | blank=True, 19 | help_text='Your form fields are available as template context. Example: "{{ first_name }} {{ last_name }} <{{ from_email }}>" if you have fields named `first_name`, `last_name`, `from_email`.', 20 | max_length=255, 21 | verbose_name="reply-to address", 22 | ), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /form_designer/migrations/0003_mail_cover_text.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations, models 2 | 3 | import form_designer.fields 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("form_designer", "0002_reply_to"), 9 | ] 10 | 11 | operations = [ 12 | migrations.AddField( 13 | model_name="formdefinition", 14 | name="mail_cover_text", 15 | field=models.TextField( 16 | help_text="Email cover text which can be included in the default email template and in the message template.", 17 | null=True, 18 | verbose_name="email cover text", 19 | blank=True, 20 | ), 21 | ), 22 | migrations.AlterField( 23 | model_name="formdefinition", 24 | name="message_template", 25 | field=form_designer.fields.TemplateTextField( 26 | help_text='Your form fields are available as template context. Example: "{{ message }}" if you have a field named `message`. To iterate over all fields, use the variable `data` (a list containing a dictionary for each form field, each containing the elements `name`, `label`, `value`). If you have set up email cover text, you can use {{ mail_cover_text }} to access it.', 27 | null=True, 28 | verbose_name="message template", 29 | blank=True, 30 | ), 31 | ), 32 | ] 33 | -------------------------------------------------------------------------------- /form_designer/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andersinno/django-form-designer-ai/104f205035681e0743372f6f22de96bc6eca9d6c/form_designer/migrations/__init__.py -------------------------------------------------------------------------------- /form_designer/settings.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | 3 | from django.conf import settings 4 | from django.core.exceptions import ImproperlyConfigured 5 | from django.utils.translation import gettext_lazy as _ 6 | 7 | STATIC_URL = os.path.join( 8 | getattr(settings, "STATIC_URL", settings.MEDIA_URL), "form_designer" 9 | ) 10 | 11 | FIELD_CLASSES = getattr( 12 | settings, 13 | "FORM_DESIGNER_FIELD_CLASSES", 14 | ( 15 | ("django.forms.CharField", _("Text")), 16 | ("django.forms.EmailField", _("E-mail address")), 17 | ("django.forms.URLField", _("Web address")), 18 | ("django.forms.IntegerField", _("Number")), 19 | ("django.forms.DecimalField", _("Decimal number")), 20 | ("django.forms.BooleanField", _("Yes/No")), 21 | ("django.forms.DateField", _("Date")), 22 | ("django.forms.DateTimeField", _("Date & time")), 23 | ("django.forms.TimeField", _("Time")), 24 | ("django.forms.ChoiceField", _("Choice")), 25 | ("django.forms.MultipleChoiceField", _("Multiple Choice")), 26 | ("django.forms.ModelChoiceField", _("Model Choice")), 27 | ("django.forms.ModelMultipleChoiceField", _("Model Multiple Choice")), 28 | ("django.forms.RegexField", _("Regex")), 29 | ("django.forms.FileField", _("File")), 30 | # ('captcha.fields.CaptchaField', _('Captcha')), 31 | ), 32 | ) 33 | 34 | WIDGET_CLASSES = getattr( 35 | settings, 36 | "FORM_DESIGNER_WIDGET_CLASSES", 37 | ( 38 | ("", _("Default")), 39 | ("django.forms.widgets.Textarea", _("Text area")), 40 | ("django.forms.widgets.PasswordInput", _("Password input")), 41 | ("django.forms.widgets.HiddenInput", _("Hidden input")), 42 | ("django.forms.widgets.RadioSelect", _("Radio button")), 43 | ("django.forms.widgets.CheckboxSelectMultiple", _("Checkbox")), 44 | ), 45 | ) 46 | 47 | EXPORTER_CLASSES = getattr( 48 | settings, 49 | "FORM_DESIGNER_EXPORTER_CLASSES", 50 | ( 51 | "form_designer.contrib.exporters.csv_exporter.CsvExporter", 52 | "form_designer.contrib.exporters.xls_exporter.XlsExporter", 53 | ), 54 | ) 55 | 56 | FORM_TEMPLATES = getattr( 57 | settings, 58 | "FORM_DESIGNER_FORM_TEMPLATES", 59 | ( 60 | ("", _("Default")), 61 | ("html/formdefinition/forms/as_p.html", _("as paragraphs")), 62 | ("html/formdefinition/forms/as_table.html", _("as table")), 63 | ("html/formdefinition/forms/as_table_h.html", _("as table (horizontal)")), 64 | ("html/formdefinition/forms/as_ul.html", _("as unordered list")), 65 | ("html/formdefinition/forms/custom.html", _("custom implementation")), 66 | ), 67 | ) 68 | 69 | # Sequence of two-tuples like (('your_app.models.ModelName', 'My Model'), ...) for 70 | # limiting the models available to ModelChoiceField and ModelMultipleChoiceField. 71 | # If None, any model can be chosen by entering it as a string 72 | CHOICE_MODEL_CHOICES = getattr(settings, "FORM_DESIGNER_CHOICE_MODEL_CHOICES", None) 73 | 74 | DEFAULT_FORM_TEMPLATE = getattr( 75 | settings, 76 | "FORM_DESIGNER_DEFAULT_FORM_TEMPLATE", 77 | "html/formdefinition/forms/as_p.html", 78 | ) 79 | 80 | # semicolon is Microsoft Excel default 81 | CSV_EXPORT_DELIMITER = getattr(settings, "FORM_DESIGNER_CSV_EXPORT_DELIMITER", ";") 82 | 83 | # include log timestamp in export 84 | CSV_EXPORT_INCLUDE_CREATED = getattr( 85 | settings, "FORM_DESIGNER_CSV_EXPORT_INCLUDE_CREATED", True 86 | ) 87 | 88 | CSV_EXPORT_INCLUDE_PK = getattr(settings, "FORM_DESIGNER_CSV_EXPORT_INCLUDE_PK", True) 89 | 90 | # include field labels/names in first row if exporting logs for one form only 91 | CSV_EXPORT_INCLUDE_HEADER = getattr( 92 | settings, "FORM_DESIGNER_CSV_EXPORT_INCLUDE_HEADER", True 93 | ) 94 | 95 | # include form title if exporting logs for more than one form 96 | CSV_EXPORT_INCLUDE_FORM = getattr( 97 | settings, "FORM_DESIGNER_CSV_EXPORT_INCLUDE_FORM", True 98 | ) 99 | 100 | CSV_EXPORT_ENCODING = getattr(settings, "FORM_DESIGNER_CSV_EXPORT_ENCODING", "utf-8") 101 | 102 | CSV_EXPORT_NULL_VALUE = getattr(settings, "FORM_DESIGNER_CSV_EXPORT_NULL_VALUE", "") 103 | 104 | SUBMIT_FLAG_NAME = getattr(settings, "FORM_DESIGNER_SUBMIT_FLAG_NAME", "submit__%s") 105 | 106 | FILE_STORAGE_CLASS = getattr(settings, "FORM_DESIGNER_FILE_STORAGE_CLASS", None) 107 | 108 | FILE_STORAGE_NAME = getattr(settings, "FORM_DESIGNER_FILE_STORAGE_NAME", None) 109 | 110 | FILE_STORAGE_DIR = "form_uploads" 111 | 112 | ALLOWED_FILE_TYPES = getattr( 113 | settings, 114 | "FORM_DESIGNER_ALLOWED_FILE_TYPES", 115 | ( 116 | "aac", 117 | "ace", 118 | "ai", 119 | "aiff", 120 | "avi", 121 | "bmp", 122 | "dir", 123 | "doc", 124 | "docx", 125 | "dmg", 126 | "eps", 127 | "fla", 128 | "flv", 129 | "gif", 130 | "gz", 131 | "hqx", 132 | "ico", 133 | "indd", 134 | "inx", 135 | "jpg", 136 | "jar", 137 | "jpeg", 138 | "md", 139 | "mov", 140 | "mp3", 141 | "mp4", 142 | "mpc", 143 | "mkv", 144 | "mpg", 145 | "mpeg", 146 | "ogg", 147 | "odg", 148 | "odf", 149 | "odp", 150 | "ods", 151 | "odt", 152 | "otf", 153 | "pdf", 154 | "png", 155 | "pps", 156 | "ppsx", 157 | "ps", 158 | "psd", 159 | "rar", 160 | "rm", 161 | "rtf", 162 | "sit", 163 | "swf", 164 | "tar", 165 | "tga", 166 | "tif", 167 | "tiff", 168 | "ttf", 169 | "txt", 170 | "wav", 171 | "wma", 172 | "wmv", 173 | "xls", 174 | "xlsx", 175 | "xml", 176 | "zip", 177 | ), 178 | ) 179 | 180 | MAX_UPLOAD_SIZE = getattr(settings, "MAX_UPLOAD_SIZE", 5242880) # 5M 181 | MAX_UPLOAD_TOTAL_SIZE = getattr(settings, "MAX_UPLOAD_TOTAL_SIZE", 10485760) # 10M 182 | 183 | VALUE_PICKLEFIELD = True 184 | 185 | DESIGNED_FORM_CLASS = getattr( 186 | settings, "FORM_DESIGNER_DESIGNED_FORM_CLASS", "form_designer.forms.DesignedForm" 187 | ) 188 | 189 | EMAIL_TEMPLATE = getattr( 190 | settings, "FORM_DESIGNER_EMAIL_TEMPLATE", "txt/formdefinition/data_message.txt" 191 | ) 192 | 193 | PUSH_MESSAGES = getattr(settings, "FORM_DESIGNER_CMS_PLUGIN_PUSH_MESSAGES", False) 194 | 195 | # reCAPTCHA settings 196 | USE_GOOGLE_RECAPTCHA = getattr(settings, "FORM_DESIGNER_USE_GOOGLE_RECAPTCHA", False) 197 | GOOGLE_RECAPTCHA_SITE_KEY = getattr(settings, "GOOGLE_RECAPTCHA_SITE_KEY", None) 198 | GOOGLE_RECAPTCHA_SECRET_KEY = getattr(settings, "GOOGLE_RECAPTCHA_SECRET_KEY", None) 199 | -------------------------------------------------------------------------------- /form_designer/signals.py: -------------------------------------------------------------------------------- 1 | from django import dispatch 2 | 3 | designedform_submit = dispatch.Signal() 4 | designedform_success = dispatch.Signal() 5 | designedform_error = dispatch.Signal() 6 | designedform_render = dispatch.Signal() 7 | -------------------------------------------------------------------------------- /form_designer/static/form_designer/js/jquery-inline-collapsible.js: -------------------------------------------------------------------------------- 1 | /* 2 | Makes all inline forms collapsible. 3 | */ 4 | 5 | jQuery(function($) { 6 | var gettext = window.gettext || function(s) { return s }; 7 | $.makeCollapsible = function(target, item, collapsible, triggerTarget, setInitStatus, setFirstStatus) 8 | { 9 | var triggerExpand = gettext('Show'); 10 | var triggerCollapse = gettext('Hide'); 11 | var triggerClass = 'collapse-expand'; 12 | var triggerLink = ''; 13 | var triggerPrepend = ' ('; 14 | var triggerAppend = ')'; 15 | 16 | $(target).find(item).each(function(i) { 17 | if ($(this).data('isCollapsible')) return; 18 | $(this).data('isCollapsible', true); 19 | 20 | $(this).find(collapsible).hide(); 21 | 22 | // trigger already exists if created with "Add another" link 23 | var trigger = $(this).find(triggerTarget).find('.'+triggerClass); 24 | if (!trigger.length) { 25 | trigger = $(triggerLink); 26 | $(this).find(triggerTarget).append(trigger); 27 | trigger.before(triggerPrepend); 28 | trigger.after(triggerAppend); 29 | } 30 | 31 | var item = this; 32 | var toggleCollapse = function(status, speed) 33 | { 34 | if (status == null) { 35 | status = !item.collapseStatus; 36 | } 37 | if (speed == null) { 38 | speed = 1; 39 | } 40 | item.collapseStatus = status; 41 | if (status) { 42 | trigger.html(triggerCollapse); 43 | $(item).find(collapsible).show(); 44 | } else { 45 | trigger.html(triggerExpand); 46 | $(item).find(collapsible).hide(); 47 | } 48 | } 49 | 50 | trigger.click(function(event) { 51 | event.preventDefault(); 52 | toggleCollapse(null, 'normal') 53 | }) 54 | 55 | // Collapse by default unless there are errors 56 | initStatus = setInitStatus != null ? setInitStatus : $(this).find('.errors').length != 0; 57 | firstStatus = setFirstStatus != null ? setFirstStatus : initStatus; 58 | 59 | toggleCollapse(i == 0 ? firstStatus : initStatus) 60 | }); 61 | }; 62 | 63 | var init = function() { 64 | $.makeCollapsible('div.inline-group', 'div.inline-related', 'fieldset', 'h3 b'); 65 | } 66 | init(); 67 | // init again when "Add another" link is clicked 68 | $('.add-row a').click(function() { 69 | init(); 70 | }) 71 | }); 72 | -------------------------------------------------------------------------------- /form_designer/static/form_designer/js/jquery-inline-fieldset-collapsible.js: -------------------------------------------------------------------------------- 1 | /* 2 | Makes all fieldsets inside of inline forms collapsible. 3 | */ 4 | 5 | jQuery(function($) { 6 | var init = function() { 7 | $('div.inline-related').each(function(i) { 8 | $.makeCollapsible(this, 'fieldset', '.form-row', 'h2', null, true) 9 | }); 10 | }; 11 | 12 | init(); 13 | // init again when "Add another" link is clicked 14 | $('.add-row a').click(function() { 15 | init(); 16 | }) 17 | }); 18 | -------------------------------------------------------------------------------- /form_designer/static/form_designer/js/jquery-inline-positioning.js: -------------------------------------------------------------------------------- 1 | /* 2 | Enables repositioning of all inline elements by drag & drop. 3 | 4 | The inline model requires is a "position" field that is blank by default. 5 | This value will be set automatically by this code snippet when dragging elements. 6 | The model instances can then be ordered by that "position" field. 7 | */ 8 | 9 | jQuery(function($) { 10 | 11 | var positionField = $.scriptUrlParam ? $.scriptUrlParam(/jquery-inline-positioning\.js(\?.*)?$/, 'positionField', 'position') : 'position'; 12 | var target = $('div.inline-group#formdefinitionfield_set-group'); 13 | var handle = 'h3 b'; 14 | var item = 'div.inline-related'; 15 | var positionInput = 'input[id$=-'+positionField+']'; 16 | var hidePositionFieldClosest = '.form-row'; 17 | 18 | var renumberAll = function() { 19 | var pos = 1; 20 | target.find(item).each(function(i) { 21 | if ($(this).find(positionInput).val() != '') { 22 | $(this).find(positionInput).val(pos); 23 | pos++; 24 | } 25 | }); 26 | }; 27 | 28 | var init = function() { 29 | target.find(item).each(function(i) { 30 | if ($(this).data('isSortable')) return; 31 | $(this).data('isSortable', true); 32 | 33 | $(this).find(handle).css('cursor', 'move'); 34 | $(this).find(handle).addClass('draggable'); 35 | $(this).find(positionInput).each(function() { 36 | if (hidePositionFieldClosest) { 37 | var hidden =$(''); 38 | hidden.val($(this).val()); 39 | $(this).closest(hidePositionFieldClosest).replaceWith(hidden); 40 | } 41 | }); 42 | $(this).find('input, select, textarea').change(function() { 43 | $(this).closest(item).find('input[id$='+positionField+']').val('X'); // mark for renumberAll() to fill in 44 | renumberAll($('div.inline-group')); 45 | }); 46 | }); 47 | } 48 | 49 | var addRow = target.find('.add-row'); 50 | addRow.remove(); 51 | var ordered = []; 52 | var unordered = []; 53 | // Initially, remove and re-append all inlines ordered by their "position" value 54 | target.find(item).each(function(i) { 55 | var initialPos = $(this).find(positionInput).val(); 56 | if (initialPos) { 57 | while (initialPos < ordered.length && ordered[initialPos]) { 58 | initialPos++; 59 | } 60 | ordered[initialPos] = this; 61 | } else { 62 | unordered[unordered.length] = this; 63 | } 64 | if(this.parentElement) this.parentElement.removeChild(this); 65 | }); 66 | for (var i = 0; i < ordered.length; i++) { 67 | var el = ordered[i]; 68 | if (el) { 69 | target.append(el); 70 | } 71 | } 72 | // Add "position"-less elements in the end 73 | for (var i = 0; i < unordered.length; i++) { 74 | var el = unordered[i]; 75 | target.append(el); 76 | } 77 | target.append(addRow); 78 | 79 | target.sortable({ 80 | containment: 'parent', 81 | /*zindex: 10, */ 82 | items: item, 83 | handle: handle, 84 | update: renumberAll, 85 | opacity: .75 86 | }); 87 | 88 | init(); 89 | // init again when "Add another" link is clicked 90 | $('.add-row a').click(function() { 91 | init(); 92 | }) 93 | 94 | }); 95 | -------------------------------------------------------------------------------- /form_designer/static/form_designer/js/jquery-inline-prepopulate-label.js: -------------------------------------------------------------------------------- 1 | /* 2 | Does exactly what `prepopulated_fields = {"label" : ('name',)}` 3 | would do, but does not URLify the value (since in this case, name is a slug field 4 | but label is a title field). 5 | */ 6 | 7 | jQuery(function($) { 8 | 9 | var target = $('div.inline-group'); 10 | var item = 'div.inline-related'; 11 | var labelSel = 'input[id*=-label]'; 12 | var nameSel = 'input[id*=-name]'; 13 | 14 | var init = function() { 15 | $(target).find(item).each(function(i) { 16 | var item = $(this); 17 | item.find(labelSel).each(function() { 18 | this._changed = item.find(nameSel).val() != $(this).val(); 19 | }); 20 | item.find(labelSel).change(function() { 21 | this._changed = true; 22 | }); 23 | item.find(nameSel).keyup(function() { 24 | labelInput = item.find(labelSel); 25 | if (!labelInput[0]._changed) { 26 | labelInput.val($(this).val()); 27 | } 28 | }); 29 | }); 30 | } 31 | 32 | init(); 33 | // init again when "Add another" link is clicked 34 | $('.add-row a').click(function() { 35 | init(); 36 | }) 37 | }); -------------------------------------------------------------------------------- /form_designer/static/form_designer/js/jquery-inline-rename.js: -------------------------------------------------------------------------------- 1 | /* 2 | Replaces the name in an inline element's header while typing it in the input of the "name" field. 3 | This way, the extra inline element's header will be named instead of numbered #4, #5 etc. 4 | */ 5 | 6 | jQuery(function($) { 7 | 8 | var nameField = $.scriptUrlParam ? $.scriptUrlParam(/jquery-inline-rename\.js(\?.*)?$/, 'nameField', 'name') : 'name'; 9 | var target = $('div.inline-group'); 10 | var item = 'div.inline-related'; 11 | var nameInput = 'input[id*=-'+nameField+']'; 12 | 13 | var init = function() { 14 | target.find(item).each(function() { 15 | var input = $(this).find(nameInput); 16 | var label = $('.inline_label', this); 17 | var rename = function(evenIfEmpty) { 18 | if (evenIfEmpty || input.val()) { 19 | label.text(input.val()); 20 | } 21 | }; 22 | input.keyup(function(event) { 23 | // Update name while typing 24 | rename(true); 25 | }); 26 | rename(false); 27 | }) 28 | } 29 | 30 | init(); 31 | // init again when "Add another" link is clicked 32 | $('.add-row a').click(function() { 33 | init(); 34 | }) 35 | }); 36 | -------------------------------------------------------------------------------- /form_designer/static/form_designer/js/jquery-url-param.js: -------------------------------------------------------------------------------- 1 | jQuery(function($) { 2 | 3 | $.urlParam = function(name, defaultValue, url) { 4 | if (!url) { 5 | url = window.location.href 6 | } 7 | var results = new RegExp('[\\?&]'+name+'=([^&#]*)').exec(url); 8 | return results ? results[1] : defaultValue; 9 | } 10 | 11 | $.scriptUrlParam = function(js, name, defaultValue) { 12 | result = defaultValue; 13 | $('head script[src]').each(function() { 14 | if (this.src.match(js)) { 15 | result = $.urlParam(name, result, this.src); 16 | } 17 | }); 18 | return result; 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /form_designer/templates/admin/form_designer/formlog/change_list.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/change_list.html" %} 2 | {% load i18n %} 3 | 4 | {% block object-tools %} 5 |
    6 | {% for item in exporters %} 7 |
  • 8 | {{ item.label }} 9 |
  • 10 | {% endfor %} 11 |
12 | {% endblock %} 13 | 14 | -------------------------------------------------------------------------------- /form_designer/templates/html/formdefinition/base.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | {{ form_definition.title}} 7 | 40 | 41 | 42 | 43 | {% block content %} 44 | {% endblock %} 45 | 46 | 47 | -------------------------------------------------------------------------------- /form_designer/templates/html/formdefinition/data_message.html: -------------------------------------------------------------------------------- 1 | {% load friendly %}{% for item in data %}{% if item.label %}{{ item.label }}{% else %}{{ item.name }}{% endif %}: {{ item.value|friendly }}
2 | {% endfor %} -------------------------------------------------------------------------------- /form_designer/templates/html/formdefinition/data_table_message.html: -------------------------------------------------------------------------------- 1 | {% load friendly %} 2 | 3 | 4 | 9 | 10 | 11 | 12 | {% for item in data %}{% if item.label %} 13 | 14 | 15 | 16 | 17 | {% endfor %} 18 |
{{ item.label }}{% else %}{{ item.name }}{% endif %}: {{ item.value|friendly|safe }}
19 | 20 | 21 | -------------------------------------------------------------------------------- /form_designer/templates/html/formdefinition/detail.html: -------------------------------------------------------------------------------- 1 | {% extends "html/formdefinition/base.html" %} 2 | {% block content %} 3 | {% if form_definition.title %}

{{ form_definition.title }}

{% endif %} 4 | {% if form_definition.body %}

{{ form_definition.body|linebreaksbr }}

{% endif %} 5 | {% if messages %} 6 |
    7 | {% for message in messages %} 8 | {{ message }} 9 | {% endfor %} 10 |
11 | {% endif %} 12 | {% include form_template %} 13 | 14 | 15 | 21 | {% endblock %} 22 | -------------------------------------------------------------------------------- /form_designer/templates/html/formdefinition/forms/as_p.html: -------------------------------------------------------------------------------- 1 | {% load friendly %} 2 |
3 | {% for entry in logs %} 4 | {% for field in entry.data %} 5 |

6 | 7 | {{ field.value|friendly }} 8 |

9 | {% endfor %} 10 | {% endfor %} 11 | {% for field in form %} 12 | {% if not field.is_hidden %} 13 | {{ field.errors }} 14 |

15 | 16 | {{ field }} 17 |

18 | {% endif %} 19 | {% endfor %} 20 |

21 | {% include "html/formdefinition/forms/includes/submit.html" %} 22 |

23 | {% for field in form.hidden_fields %} 24 | {{ field }} 25 | {% endfor %} 26 | {% if form_definition.method == "POST" %}{% csrf_token %}{% endif %} 27 |
28 | -------------------------------------------------------------------------------- /form_designer/templates/html/formdefinition/forms/as_table.html: -------------------------------------------------------------------------------- 1 | {% load friendly %} 2 |
3 | 4 | 5 | {% for entry in logs %} 6 | {% for field in entry.data %} 7 | 8 | 9 | 10 | 11 | {% endfor %} 12 | {% endfor %} 13 | {% for field in form %} 14 | {% if not field.is_hidden %} 15 | 16 | 19 | 23 | 24 | {% endif %} 25 | {% endfor %} 26 | 27 | 28 | 29 | 30 | 31 |
{{ field.label }}{{ field.value|friendly }}
17 | 18 | 20 | {{ field.errors }} 21 | {{ field }} 22 |
 {% include "html/formdefinition/forms/includes/submit.html" %}
32 | {% for field in form.hidden_fields %} 33 | {{ field }} 34 | {% endfor %} 35 | {% if form_definition.method == "POST" %}{% csrf_token %}{% endif %} 36 |
37 | -------------------------------------------------------------------------------- /form_designer/templates/html/formdefinition/forms/as_table_h.html: -------------------------------------------------------------------------------- 1 | {% load friendly %} 2 |
3 | 4 | 5 | {% for field in form %} 6 | {% if not field.is_hidden %} 7 | 10 | {% endif %} 11 | {% endfor %} 12 | 13 | 14 | {% for entry in logs %} 15 | 16 | {% for field in entry.data %} 17 | 18 | {% endfor %} 19 | 20 | {% endfor %} 21 | 22 | {% for field in form %} 23 | {% if not field.is_hidden %} 24 | 28 | {% endif %} 29 | {% endfor %} 30 | 31 | 32 |
8 | 9 |
{{ field.value|friendly }}
25 | {{ field.errors }} 26 | {{ field }} 27 |
33 |

34 | {% include "html/formdefinition/forms/includes/submit.html" %} 35 |

36 | {% for field in form.hidden_fields %} 37 | {{ field }} 38 | {% endfor %} 39 | {% if form_definition.method == "POST" %}{% csrf_token %}{% endif %} 40 |
41 | -------------------------------------------------------------------------------- /form_designer/templates/html/formdefinition/forms/as_ul.html: -------------------------------------------------------------------------------- 1 | {% load friendly %} 2 |
3 |
    4 | {% for entry in logs %} 5 | {% for field in entry.data %} 6 |
  • 7 | 8 | {{ field.value|friendly }} 9 |
  • 10 | {% endfor %} 11 | {% endfor %} 12 | {% for field in form %} 13 | {% if not field.is_hidden %} 14 |
  • 15 | {{ field.errors }} 16 | 17 | {{ field }} 18 |
  • 19 | {% endif %} 20 | {% endfor %} 21 |
22 |

23 | {% include "html/formdefinition/forms/includes/submit.html" %} 24 |

25 | {% for field in form.hidden_fields %} 26 | {{ field }} 27 | {% endfor %} 28 | {% if form_definition.method == "POST" %}{% csrf_token %}{% endif %} 29 |
30 | -------------------------------------------------------------------------------- /form_designer/templates/html/formdefinition/forms/custom.html: -------------------------------------------------------------------------------- 1 | {% create your own custom.html to override this template %} 2 | {% include "html/formdefinition/forms/as_p.html" %} 3 | -------------------------------------------------------------------------------- /form_designer/templates/html/formdefinition/forms/includes/submit.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% if use_google_recaptcha %} 3 | 4 |
5 | {% endif %} 6 | 7 | -------------------------------------------------------------------------------- /form_designer/templates/txt/formdefinition/data_message.txt: -------------------------------------------------------------------------------- 1 | {% load friendly %}{% for item in data %}{% if item.label %}{{ item.label }}{% else %}{{ item.name }}{% endif %}: {{ item.value|friendly|safe }} 2 | 3 | {% endfor %} -------------------------------------------------------------------------------- /form_designer/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andersinno/django-form-designer-ai/104f205035681e0743372f6f22de96bc6eca9d6c/form_designer/templatetags/__init__.py -------------------------------------------------------------------------------- /form_designer/templatetags/friendly.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | from django.db.models.query import QuerySet 3 | from django.template.defaultfilters import yesno 4 | from django.utils.encoding import force_str 5 | 6 | register = template.Library() 7 | 8 | # Returns a more "human-friendly" representation of value than repr() 9 | 10 | 11 | def friendly(value, null_value=None): 12 | if value is None and null_value is not None: 13 | return null_value 14 | if isinstance(value, (QuerySet, list)): 15 | value = ", ".join(force_str(object) for object in value) 16 | if isinstance(value, bool): 17 | value = yesno(value) 18 | if hasattr(value, "url"): 19 | value = value.url 20 | return force_str(value) 21 | 22 | 23 | 24 | register.filter(friendly) 25 | -------------------------------------------------------------------------------- /form_designer/templatetags/widget_type.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | 3 | register = template.Library() 4 | 5 | 6 | @register.filter("field_type") 7 | def field_type(obj): 8 | return obj.__class__.__name__ 9 | -------------------------------------------------------------------------------- /form_designer/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andersinno/django-form-designer-ai/104f205035681e0743372f6f22de96bc6eca9d6c/form_designer/tests/__init__.py -------------------------------------------------------------------------------- /form_designer/tests/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from django.utils.crypto import get_random_string 3 | 4 | from form_designer.models import FormLog 5 | 6 | 7 | @pytest.fixture() 8 | def greeting_form(): 9 | from form_designer.models import FormDefinition, FormDefinitionField 10 | 11 | fd = FormDefinition.objects.create( 12 | name=get_random_string(12), 13 | mail_to="test@example.com", 14 | mail_subject="Someone sent you a greeting: {{ greeting }}", 15 | mail_reply_to="Greeting Bot ", 16 | ) 17 | FormDefinitionField.objects.create( 18 | form_definition=fd, 19 | name="greeting", 20 | label="Greeting", 21 | field_class="django.forms.CharField", 22 | required=True, 23 | ) 24 | FormDefinitionField.objects.create( 25 | form_definition=fd, 26 | name="upload", 27 | field_class="django.forms.FileField", 28 | required=False, 29 | ) 30 | return fd 31 | 32 | 33 | @pytest.fixture() 34 | def greeting_form_with_log(admin_user, greeting_form): 35 | log = FormLog.objects.create( 36 | form_definition=greeting_form, 37 | created_by=admin_user, 38 | ) 39 | log.values.create(field_name="greeting", value=get_random_string(12)) 40 | return greeting_form 41 | -------------------------------------------------------------------------------- /form_designer/tests/test_admin.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from django.forms.models import model_to_dict 3 | from django.utils.crypto import get_random_string 4 | 5 | from form_designer.models import FormDefinition 6 | 7 | 8 | @pytest.mark.django_db 9 | def test_admin_list_view_renders(admin_client, greeting_form): 10 | assert ( 11 | greeting_form.name 12 | in admin_client.get("/admin/form_designer/formdefinition/").content.decode() 13 | ) 14 | 15 | 16 | @pytest.mark.django_db 17 | def test_admin_create_view_renders_add(admin_client): 18 | assert admin_client.get("/admin/form_designer/formdefinition/add/").content 19 | 20 | 21 | @pytest.mark.django_db 22 | @pytest.mark.parametrize("n_fields", range(5)) 23 | def test_admin_create_view_creates_form(admin_client, n_fields): 24 | name = get_random_string(12) 25 | data = { 26 | "_save": "Save", 27 | "action": "", 28 | "allow_get_initial": "on", 29 | "body": "", 30 | "error_message": "", 31 | "form_template_name": "", 32 | "formdefinitionfield_set-INITIAL_FORMS": "0", 33 | "formdefinitionfield_set-MAX_NUM_FORMS": "1000", 34 | "formdefinitionfield_set-MIN_NUM_FORMS": "0", 35 | "formdefinitionfield_set-TOTAL_FORMS": n_fields, 36 | "log_data": "on", 37 | "mail_from": "", 38 | "mail_subject": "", 39 | "mail_to": "", 40 | "mail_uploaded_files": "on", 41 | "message_template": "", 42 | "method": "POST", 43 | "name": name, 44 | "save_uploaded_files": "on", 45 | "submit_label": "", 46 | "success_clear": "on", 47 | "success_message": "", 48 | "success_redirect": "on", 49 | "title": "", 50 | } 51 | 52 | for i in range(n_fields): 53 | data.update( 54 | { 55 | key.replace("NUM", str(i)): value 56 | for (key, value) in { 57 | "formdefinitionfield_set-NUM-field_class": "django.forms.CharField", 58 | "formdefinitionfield_set-NUM-include_result": "on", 59 | "formdefinitionfield_set-NUM-label": "test %d" % i, 60 | "formdefinitionfield_set-NUM-name": "test%d" % i, 61 | "formdefinitionfield_set-NUM-position": i, 62 | "formdefinitionfield_set-NUM-required": "on", 63 | }.items() 64 | } 65 | ) 66 | 67 | admin_client.post("/admin/form_designer/formdefinition/add/", data=data) 68 | 69 | fd = FormDefinition.objects.get(name=name) 70 | assert fd.formdefinitionfield_set.count() == n_fields 71 | for key, value in model_to_dict(fd).items(): # Verify our posted data 72 | if key not in data: 73 | continue 74 | if value is True: 75 | assert data[key] == "on" 76 | else: 77 | if data[key] == "": 78 | assert data[key] == value or value is None 79 | else: 80 | assert data[key] == value 81 | 82 | 83 | @pytest.mark.django_db 84 | def test_admin_list_view_renders_formlog(admin_client, greeting_form_with_log): 85 | log = greeting_form_with_log.logs.first() 86 | greeting_value = log.data[0]["value"] 87 | assert ( 88 | greeting_value 89 | in admin_client.get("/admin/form_designer/formlog/").content.decode() 90 | ) 91 | 92 | 93 | @pytest.mark.django_db 94 | def test_admin_create_view_renders(admin_client, greeting_form_with_log): 95 | assert admin_client.get("/admin/form_designer/formlog/add/").content 96 | 97 | 98 | @pytest.mark.django_db 99 | @pytest.mark.parametrize("format", ("CSV", "XLS")) 100 | def test_admin_export_view(admin_client, greeting_form_with_log, format): 101 | assert admin_client.get(f"/admin/form_designer/formlog/export/{format}/").content 102 | -------------------------------------------------------------------------------- /form_designer/tests/test_basics.py: -------------------------------------------------------------------------------- 1 | from base64 import b64decode 2 | 3 | import pytest 4 | from django.contrib.auth.models import AnonymousUser 5 | from django.contrib.messages.storage.base import BaseStorage 6 | from django.core import mail 7 | from django.core.files.base import ContentFile, File 8 | from django.utils.crypto import get_random_string 9 | 10 | from form_designer import settings as fd_settings 11 | from form_designer.contrib.exporters.csv_exporter import CsvExporter 12 | from form_designer.contrib.exporters.xls_exporter import XlsExporter 13 | from form_designer.forms import DesignedForm 14 | from form_designer.models import FormLog, FormValue 15 | from form_designer.views import process_form 16 | 17 | # https://raw.githubusercontent.com/mathiasbynens/small/master/jpeg.jpg 18 | 19 | VERY_SMALL_JPEG = b64decode( 20 | "/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgK" 21 | "CgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/" 22 | "yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=" 23 | ) 24 | 25 | 26 | class OverriddenDesignedForm(DesignedForm): 27 | def clean_greeting(self): 28 | return self.cleaned_data["greeting"].upper() 29 | 30 | 31 | @pytest.mark.django_db 32 | @pytest.mark.parametrize("push_messages", (False, True)) 33 | @pytest.mark.parametrize("valid_data", (False, True)) 34 | @pytest.mark.parametrize("method", ("GET", "POST")) 35 | @pytest.mark.parametrize("anon", (False, True)) 36 | @pytest.mark.parametrize("override_form", (False, "settings", "kwarg")) 37 | def test_simple_form( 38 | monkeypatch, 39 | rf, 40 | admin_user, 41 | greeting_form, 42 | push_messages, 43 | valid_data, 44 | method, 45 | anon, 46 | override_form, 47 | ): 48 | fd = greeting_form 49 | message = f"zzz-å{get_random_string(12)}Ö" 50 | data = { 51 | "greeting": message, 52 | "upload": ContentFile(VERY_SMALL_JPEG, name="hello.jpg"), 53 | fd.submit_flag_name: "true", 54 | } 55 | if not valid_data: 56 | data.pop("greeting") 57 | 58 | if method == "POST": 59 | request = rf.post("/", data) 60 | elif method == "GET": 61 | data.pop("upload") # can't upload via GET 62 | request = rf.get("/", data) 63 | 64 | request.user = AnonymousUser() if anon else admin_user 65 | request._messages = BaseStorage(request) 66 | kwargs = dict( 67 | push_messages=push_messages, 68 | disable_redirection=True, 69 | ) 70 | if override_form == "kwarg": 71 | kwargs["form_class"] = OverriddenDesignedForm 72 | elif override_form == "settings": 73 | # Can't use the pytest-django settings fixture, since `form_designer.settings` 74 | # has non-lazy copies of Django settings taken at that module's import time. 75 | monkeypatch.setattr( 76 | fd_settings, 77 | "DESIGNED_FORM_CLASS", 78 | "form_designer.tests.test_basics.OverriddenDesignedForm", 79 | ) 80 | 81 | context = process_form(request, fd, **kwargs) 82 | assert context["form_success"] == valid_data 83 | 84 | # Test that a message was (or was not) pushed 85 | assert len(request._messages._queued_messages) == int(push_messages) 86 | 87 | if valid_data: 88 | if ( 89 | override_form 90 | ): # If we've overridden the form, we expect an uppercase message 91 | message = message.upper() 92 | 93 | # Test that the form log was saved: 94 | flog = FormLog.objects.get(form_definition=fd) 95 | assert flog == context["form_log"] # (and it's the same object in the context) 96 | name_to_value = {d["name"]: d["value"] for d in flog.data} 97 | assert name_to_value["greeting"] == message 98 | if name_to_value.get("upload"): 99 | assert isinstance(name_to_value["upload"], File) 100 | if not anon: 101 | assert flog.created_by == admin_user 102 | 103 | # Test that the email was sent: 104 | sent_email = mail.outbox[-1] 105 | 106 | assert ( 107 | message in sent_email.subject 108 | ) # (since we customized the subject with a template) 109 | assert "greetingbot" in sent_email.message().get( 110 | "Reply-To" 111 | ) # (since we customized the reply-to address) 112 | 113 | 114 | @pytest.mark.django_db 115 | @pytest.mark.parametrize( 116 | "exporter", 117 | [ 118 | CsvExporter, 119 | XlsExporter, 120 | ], 121 | ) 122 | @pytest.mark.parametrize("n_logs", range(5)) 123 | def test_export(rf, greeting_form, exporter, n_logs): 124 | if exporter is XlsExporter: 125 | pytest.importorskip("xlsx") 126 | message = "Térve" 127 | for n in range(n_logs): 128 | fl = FormLog.objects.create(form_definition=greeting_form) 129 | FormValue.objects.create( 130 | form_log=fl, 131 | field_name="greeting", 132 | value="%s %d" % (message, n + 1), 133 | ) 134 | 135 | resp = exporter(greeting_form).export( 136 | request=rf.get("/"), 137 | queryset=FormLog.objects.filter(form_definition=greeting_form), 138 | ) 139 | if "csv" in resp["content-type"]: 140 | # TODO: Improve CSV test? 141 | csv_data = resp.content.decode("utf8").splitlines() 142 | if n_logs > 0: # The file will be empty if no logs exist 143 | assert csv_data[0].startswith("Created") 144 | assert "Greeting" in csv_data[0] 145 | for i in range(1, n_logs): 146 | assert message in csv_data[i] 147 | assert (f"{i}") in csv_data[i] 148 | -------------------------------------------------------------------------------- /form_designer/tests/test_messages.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from form_designer.forms import DesignedForm 4 | 5 | 6 | @pytest.mark.django_db 7 | def test_custom_message_template(greeting_form): 8 | """ 9 | Test that custom message templates work as expected. 10 | """ 11 | greeting_form.message_template = "{{ greeting }}, friend!" 12 | form = DesignedForm( 13 | greeting_form, 14 | data={ 15 | "greeting": "Hello", 16 | }, 17 | ) 18 | assert form.is_valid() 19 | greeting_form.log(form) 20 | mail = greeting_form.send_mail(form) 21 | assert mail.body == "Hello, friend!" 22 | -------------------------------------------------------------------------------- /form_designer/uploads.py: -------------------------------------------------------------------------------- 1 | import os 2 | from types import SimpleNamespace 3 | 4 | from django.core.files.base import File 5 | from django.db.models.fields.files import FieldFile 6 | from django.forms.forms import NON_FIELD_ERRORS 7 | from django.template.defaultfilters import filesizeformat 8 | from django.utils.translation import gettext_lazy as _ 9 | 10 | from form_designer import settings as app_settings 11 | from form_designer.utils import get_random_hash 12 | 13 | 14 | def get_storage(): 15 | if app_settings.FILE_STORAGE_NAME: # Django 4.2+ 16 | from django.core.files.storage import storages 17 | return storages[app_settings.FILE_STORAGE_NAME] 18 | if app_settings.FILE_STORAGE_CLASS: 19 | from django.core.files.storage import get_storage_class 20 | return get_storage_class(app_settings.FILE_STORAGE_CLASS)() 21 | from django.core.files.storage import default_storage 22 | return default_storage 23 | 24 | 25 | def clean_files(form): 26 | total_upload_size = 0 27 | for field in form.file_fields: 28 | uploaded_file = form.cleaned_data.get(field.name, None) 29 | msg = None 30 | if uploaded_file is None: 31 | if field.required: 32 | msg = _("This field is required.") 33 | else: 34 | continue 35 | else: 36 | file_size = uploaded_file.size 37 | total_upload_size += file_size 38 | if ( 39 | os.path.splitext(uploaded_file.name)[1].lstrip(".").lower() not in app_settings.ALLOWED_FILE_TYPES 40 | ): 41 | msg = _("This file type is not allowed.") 42 | elif file_size > app_settings.MAX_UPLOAD_SIZE: 43 | msg = _( 44 | "Please keep file size under %(max_size)s. Current size is %(size)s." 45 | ) % { 46 | "max_size": filesizeformat(app_settings.MAX_UPLOAD_SIZE), 47 | "size": filesizeformat(file_size), 48 | } 49 | if msg: 50 | form._errors[field.name] = form.error_class([msg]) 51 | 52 | if total_upload_size > app_settings.MAX_UPLOAD_TOTAL_SIZE: 53 | msg = _( 54 | "Please keep total file size under %(max)s. Current total size is %(current)s." 55 | ) % { 56 | "max": filesizeformat(app_settings.MAX_UPLOAD_TOTAL_SIZE), 57 | "current": filesizeformat(total_upload_size), 58 | } 59 | 60 | if NON_FIELD_ERRORS in form._errors: 61 | form._errors[NON_FIELD_ERRORS].append(msg) 62 | else: 63 | form._errors[NON_FIELD_ERRORS] = form.error_class([msg]) 64 | 65 | return form.cleaned_data 66 | 67 | 68 | def handle_uploaded_files(form_definition, form): 69 | files = [] 70 | if form_definition.save_uploaded_files and len(form.file_fields): 71 | storage = get_storage() 72 | secret_hash = get_random_hash(10) 73 | for field in form.file_fields: 74 | uploaded_file = form.cleaned_data.get(field.name, None) 75 | if uploaded_file is None: 76 | continue 77 | valid_file_name = storage.get_valid_name(uploaded_file.name) 78 | root, ext = os.path.splitext(valid_file_name) 79 | filename = storage.get_available_name( 80 | os.path.join( 81 | app_settings.FILE_STORAGE_DIR, 82 | form_definition.name, 83 | f"{root}_{secret_hash}{ext}", 84 | ) 85 | ) 86 | storage.save(filename, uploaded_file) 87 | form.cleaned_data[field.name] = StoredUploadedFile(name=filename) 88 | files.append(storage.path(filename)) 89 | return files 90 | 91 | 92 | class StoredUploadedFile(FieldFile): 93 | """ 94 | A wrapper for uploaded files that is compatible to the FieldFile class, i.e. 95 | you can use instances of this class in templates just like you use the value 96 | of FileFields (e.g. `{{ my_file.url }}`) 97 | """ 98 | 99 | def __init__(self, name): 100 | File.__init__(self, None, name) 101 | self.field = SimpleNamespace(storage=get_storage()) 102 | self.instance = None 103 | 104 | def save(self, *args, **kwargs): 105 | raise NotImplementedError("Static files are read-only") # pragma: no cover 106 | 107 | def delete(self, *args, **kwargs): 108 | raise NotImplementedError("Static files are read-only") # pragma: no cover 109 | 110 | def __str__(self): 111 | return self.name 112 | -------------------------------------------------------------------------------- /form_designer/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import re_path 2 | 3 | urlpatterns = [ 4 | re_path( 5 | r"^(?P[-\w]+)/$", 6 | "form_designer.views.detail", 7 | name="form_designer_detail", 8 | ), 9 | re_path( 10 | r"^h/(?P[-\w]+)/$", 11 | "form_designer.views.detail_by_hash", 12 | name="form_designer_detail_by_hash", 13 | ), 14 | ] 15 | -------------------------------------------------------------------------------- /form_designer/utils.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | 3 | from django.template import Context, Template, TemplateSyntaxError 4 | from django.utils.crypto import get_random_string 5 | 6 | 7 | def get_random_hash(length=32): 8 | return hashlib.sha1(get_random_string(12).encode("utf8")).hexdigest()[:length] 9 | 10 | 11 | def string_template_replace(text, context_dict): 12 | try: 13 | t = Template(text) 14 | return t.render(Context(context_dict)) 15 | except TemplateSyntaxError: 16 | return text 17 | -------------------------------------------------------------------------------- /form_designer/views.py: -------------------------------------------------------------------------------- 1 | import json 2 | from urllib.request import Request, urlopen 3 | 4 | from django.contrib import messages 5 | from django.http import HttpResponseRedirect 6 | from django.shortcuts import get_object_or_404, render 7 | from django.template.context_processors import csrf 8 | from django.utils.http import urlencode 9 | from django.utils.module_loading import import_string 10 | from django.utils.translation import gettext_lazy as _ 11 | 12 | from form_designer import settings as app_settings 13 | from form_designer.models import FormDefinition 14 | from form_designer.signals import ( 15 | designedform_error, 16 | designedform_render, 17 | designedform_submit, 18 | designedform_success, 19 | ) 20 | from form_designer.uploads import handle_uploaded_files 21 | 22 | 23 | def get_designed_form_class(): 24 | return import_string(app_settings.DESIGNED_FORM_CLASS) 25 | 26 | 27 | def check_recaptcha(request, context, push_messages): 28 | is_valid = True 29 | if not app_settings.USE_GOOGLE_RECAPTCHA: 30 | return is_valid 31 | """ Begin reCAPTCHA validation """ 32 | recaptcha_response = request.POST.get("g-recaptcha-response") 33 | url = "https://www.google.com/recaptcha/api/siteverify" 34 | values = { 35 | "secret": app_settings.GOOGLE_RECAPTCHA_SECRET_KEY, 36 | "response": recaptcha_response, 37 | } 38 | data = urlencode(values).encode("utf-8") 39 | req = Request(url, data) 40 | response = urlopen(req) 41 | result = json.load(response) 42 | """ End reCAPTCHA validation """ 43 | if not result["success"]: 44 | is_valid = False 45 | error_message = _("Invalid reCAPTCHA.") 46 | if push_messages: 47 | messages.error(request, error_message) 48 | return is_valid 49 | 50 | 51 | def update_recaptcha_context(context): 52 | if not app_settings.USE_GOOGLE_RECAPTCHA: 53 | return 54 | context.update( 55 | { 56 | "use_google_recaptcha": True, 57 | "google_recaptcha_site_key": app_settings.GOOGLE_RECAPTCHA_SITE_KEY, 58 | } 59 | ) 60 | 61 | 62 | def process_form( # noqa: C901 63 | request, 64 | form_definition, 65 | extra_context=None, 66 | disable_redirection=False, 67 | push_messages=True, 68 | form_class=None, 69 | ): 70 | if extra_context is None: 71 | extra_context = {} 72 | if form_class is None: 73 | form_class = get_designed_form_class() 74 | context = extra_context 75 | success_message = form_definition.success_message or _( 76 | "Thank you, the data was submitted successfully." 77 | ) 78 | error_message = form_definition.error_message or _( 79 | "The data could not be submitted, please try again." 80 | ) 81 | form_error = False 82 | form_success = False 83 | is_submit = False 84 | # If the form has been submitted... 85 | if request.method == "POST" and request.POST.get(form_definition.submit_flag_name): 86 | form = form_class(form_definition, None, request.POST, request.FILES) 87 | is_submit = True 88 | if request.method == "GET" and request.GET.get(form_definition.submit_flag_name): 89 | form = form_class(form_definition, None, request.GET) 90 | is_submit = True 91 | 92 | if is_submit: 93 | designedform_submit.send( 94 | sender=process_form, 95 | context=context, 96 | form_definition=form_definition, 97 | request=request, 98 | ) 99 | recaptcha_is_valid = check_recaptcha(request, context, push_messages) 100 | if form.is_valid() and recaptcha_is_valid: 101 | # Handle file uploads using storage object 102 | files = handle_uploaded_files(form_definition, form) 103 | 104 | # Successful submission 105 | if push_messages: 106 | messages.success(request, success_message) 107 | form_success = True 108 | 109 | designedform_success.send( 110 | sender=process_form, 111 | context=context, 112 | form_definition=form_definition, 113 | request=request, 114 | ) 115 | 116 | if form_definition.log_data: 117 | context["form_log"] = form_definition.log(form, request.user) 118 | if form_definition.mail_to: 119 | context["form_mail_message"] = form_definition.send_mail(form, files) 120 | if form_definition.success_redirect and not disable_redirection: 121 | return HttpResponseRedirect(form_definition.action or "?") 122 | if form_definition.success_clear: 123 | form = form_class(form_definition) # clear form 124 | else: 125 | form_error = True 126 | designedform_error.send( 127 | sender=process_form, 128 | context=context, 129 | form_definition=form_definition, 130 | request=request, 131 | ) 132 | if push_messages: 133 | messages.error(request, error_message) 134 | else: 135 | if form_definition.allow_get_initial: 136 | form = form_class(form_definition, initial_data=request.GET) 137 | else: 138 | form = form_class(form_definition) 139 | designedform_render.send( 140 | sender=process_form, 141 | context=context, 142 | form_definition=form_definition, 143 | request=request, 144 | ) 145 | 146 | context.update( 147 | { 148 | "form_error": form_error, 149 | "form_success": form_success, 150 | "form_success_message": success_message, 151 | "form_error_message": error_message, 152 | "form": form, 153 | "form_definition": form_definition, 154 | } 155 | ) 156 | context.update(csrf(request)) 157 | update_recaptcha_context(context) 158 | 159 | if form_definition.display_logged: 160 | logs = form_definition.logs.all().order_by("created") 161 | context.update({"logs": logs}) 162 | 163 | return context 164 | 165 | 166 | def _form_detail_view(request, form_definition): 167 | result = process_form(request, form_definition) 168 | if isinstance(result, HttpResponseRedirect): 169 | return result 170 | result.update( 171 | { 172 | "form_template": form_definition.form_template_name 173 | or app_settings.DEFAULT_FORM_TEMPLATE 174 | } 175 | ) 176 | return render("html/formdefinition/detail.html", result) 177 | 178 | 179 | def detail(request, object_name): 180 | form_definition = get_object_or_404( 181 | FormDefinition, name=object_name, require_hash=False 182 | ) 183 | return _form_detail_view(request, form_definition) 184 | 185 | 186 | def detail_by_hash(request, public_hash): 187 | form_definition = get_object_or_404(FormDefinition, public_hash=public_hash) 188 | return _form_detail_view(request, form_definition) 189 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["hatchling"] 3 | build-backend = "hatchling.build" 4 | 5 | [project] 6 | name = "django-form-designer-ai" 7 | dynamic = ["version"] 8 | readme = "README.rst" 9 | license = "BSD-3-Clause" 10 | requires-python = ">=3.8" 11 | classifiers = [ 12 | "Development Status :: 4 - Beta", 13 | "Framework :: Django", 14 | "Intended Audience :: Developers", 15 | "License :: OSI Approved :: BSD License", 16 | "Operating System :: OS Independent", 17 | "Programming Language :: Python", 18 | "Topic :: Internet :: WWW/HTTP", 19 | ] 20 | dependencies = [ 21 | "django>=3.0", 22 | "django-picklefield", 23 | ] 24 | 25 | [project.urls] 26 | Homepage = "https://github.com/andersinno/django-form-designer-ai" 27 | 28 | [tool.hatch.build] 29 | include = [ 30 | "form_designer/**/*.py", 31 | "form_designer/templates/", 32 | "form_designer/static/", 33 | "form_designer/locale/", 34 | ] 35 | 36 | [tool.hatch.version] 37 | path = "form_designer/__init__.py" 38 | 39 | [tool.hatch.build.targets.sdist] 40 | include = ["/form_designer"] 41 | 42 | [tool.pytest.ini_options] 43 | django_settings_module = "dfd_tests.settings" 44 | norecursedirs = ["bower_components", "node_modules", ".git", "venv*"] 45 | doctest_optionflags = [ 46 | "NORMALIZE_WHITESPACE", 47 | "IGNORE_EXCEPTION_DETAIL", 48 | "ALLOW_UNICODE" 49 | ] 50 | filterwarnings = [ 51 | "ignore:.+default_app_config.+", 52 | "once::DeprecationWarning", 53 | "once::PendingDeprecationWarning", 54 | "ignore:.+PickledObjectField.from_db_value.+" 55 | ] 56 | 57 | [tool.ruff] 58 | exclude = ["migrations", "node_modules", ".tox"] 59 | ignore = [] 60 | line-length = 120 61 | select = ["C9", "E", "F", "W", "I"] 62 | 63 | [tool.ruff.mccabe] 64 | max-complexity = 10 65 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = 3 | py{38,39,310}-django32 4 | py{39,310}-django{40,41} 5 | py{310,311,312,313}-django{42} 6 | 7 | skip_missing_interpreters = true 8 | 9 | [testenv] 10 | deps = 11 | django32: Django>=3.2,<3.3 12 | django40: Django>=4.0,<4.1 13 | django41: Django>=4.1,<4.2 14 | django42: Django>=4.2,<4.3 15 | 16 | pytest-django 17 | django-picklefield 18 | mysqlclient 19 | 20 | pytest-cov 21 | pytz 22 | xlwt 23 | 24 | commands = 25 | pip install pytest-django 26 | py.test -ra --cov form_designer --cov-report term --cov-report html form_designer {posargs} 27 | setenv = 28 | DEBUG = 1 29 | PYTHONPATH = {toxinidir} 30 | DJANGO_SETTINGS_MODULE = dfd_tests.settings 31 | passenv = 32 | DATABASE* 33 | 34 | [gh-actions] 35 | python = 36 | 3.8: py38 37 | 3.9: py39 38 | 3.10: py310 39 | 3.11: py311 40 | 3.12: py312 41 | 3.13: py313 42 | --------------------------------------------------------------------------------