wagtail-flexible-forms-hg 2.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wagtail_flexible_forms_hg-2.2.0/LICENSE.txt +30 -0
- wagtail_flexible_forms_hg-2.2.0/PKG-INFO +77 -0
- wagtail_flexible_forms_hg-2.2.0/README.md +22 -0
- wagtail_flexible_forms_hg-2.2.0/pyproject.toml +79 -0
- wagtail_flexible_forms_hg-2.2.0/setup.cfg +4 -0
- wagtail_flexible_forms_hg-2.2.0/wagtail_flexible_forms/__init__.py +1 -0
- wagtail_flexible_forms_hg-2.2.0/wagtail_flexible_forms/blocks.py +288 -0
- wagtail_flexible_forms_hg-2.2.0/wagtail_flexible_forms/edit_handlers.py +35 -0
- wagtail_flexible_forms_hg-2.2.0/wagtail_flexible_forms/models.py +916 -0
- wagtail_flexible_forms_hg-2.2.0/wagtail_flexible_forms/wagtail_hooks.py +38 -0
- wagtail_flexible_forms_hg-2.2.0/wagtail_flexible_forms_hg.egg-info/PKG-INFO +77 -0
- wagtail_flexible_forms_hg-2.2.0/wagtail_flexible_forms_hg.egg-info/SOURCES.txt +13 -0
- wagtail_flexible_forms_hg-2.2.0/wagtail_flexible_forms_hg.egg-info/dependency_links.txt +1 -0
- wagtail_flexible_forms_hg-2.2.0/wagtail_flexible_forms_hg.egg-info/requires.txt +1 -0
- wagtail_flexible_forms_hg-2.2.0/wagtail_flexible_forms_hg.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018, NoriPyt
|
|
4
|
+
Copyright (c) 2025, CodeRed LLC
|
|
5
|
+
All rights reserved.
|
|
6
|
+
|
|
7
|
+
Redistribution and use in source and binary forms, with or without
|
|
8
|
+
modification, are permitted provided that the following conditions are met:
|
|
9
|
+
|
|
10
|
+
* Redistributions of source code must retain the above copyright notice, this
|
|
11
|
+
list of conditions and the following disclaimer.
|
|
12
|
+
|
|
13
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
|
14
|
+
this list of conditions and the following disclaimer in the documentation
|
|
15
|
+
and/or other materials provided with the distribution.
|
|
16
|
+
|
|
17
|
+
* Neither the name of the copyright holder nor the names of its
|
|
18
|
+
contributors may be used to endorse or promote products derived from
|
|
19
|
+
this software without specific prior written permission.
|
|
20
|
+
|
|
21
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
22
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
23
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
24
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
25
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
26
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
27
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
28
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
29
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
30
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: wagtail-flexible-forms-hg
|
|
3
|
+
Version: 2.2.0
|
|
4
|
+
Summary: Wagtail StreamField-based form builder.
|
|
5
|
+
Author: Bertrand Bordage
|
|
6
|
+
Author-email: CodeRed LLC <info@coderedcorp.com>
|
|
7
|
+
License: BSD 3-Clause License
|
|
8
|
+
|
|
9
|
+
Copyright (c) 2018, NoriPyt
|
|
10
|
+
Copyright (c) 2025, CodeRed LLC
|
|
11
|
+
All rights reserved.
|
|
12
|
+
|
|
13
|
+
Redistribution and use in source and binary forms, with or without
|
|
14
|
+
modification, are permitted provided that the following conditions are met:
|
|
15
|
+
|
|
16
|
+
* Redistributions of source code must retain the above copyright notice, this
|
|
17
|
+
list of conditions and the following disclaimer.
|
|
18
|
+
|
|
19
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
|
20
|
+
this list of conditions and the following disclaimer in the documentation
|
|
21
|
+
and/or other materials provided with the distribution.
|
|
22
|
+
|
|
23
|
+
* Neither the name of the copyright holder nor the names of its
|
|
24
|
+
contributors may be used to endorse or promote products derived from
|
|
25
|
+
this software without specific prior written permission.
|
|
26
|
+
|
|
27
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
28
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
29
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
30
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
31
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
32
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
33
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
34
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
35
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
36
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
37
|
+
|
|
38
|
+
Project-URL: Source, https://github.com/happygrizzly/wagtail-flexible-forms
|
|
39
|
+
Classifier: Intended Audience :: Developers
|
|
40
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
41
|
+
Classifier: Natural Language :: English
|
|
42
|
+
Classifier: Operating System :: OS Independent
|
|
43
|
+
Classifier: Programming Language :: Python :: 3
|
|
44
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
45
|
+
Classifier: Framework :: Django
|
|
46
|
+
Classifier: Framework :: Wagtail
|
|
47
|
+
Classifier: Framework :: Wagtail :: 6
|
|
48
|
+
Classifier: Framework :: Wagtail :: 7
|
|
49
|
+
Classifier: Framework :: Wagtail :: 8
|
|
50
|
+
Requires-Python: >=3.9
|
|
51
|
+
Description-Content-Type: text/markdown
|
|
52
|
+
License-File: LICENSE.txt
|
|
53
|
+
Requires-Dist: wagtail<8.2,>=6
|
|
54
|
+
Dynamic: license-file
|
|
55
|
+
|
|
56
|
+
Wagtail Flexible Forms
|
|
57
|
+
======================
|
|
58
|
+
|
|
59
|
+
Wagtail flexible forms provides a more robust StreamField-based form builder (sometimes called a "StreamForm") with similar functionality to `wagtail.contrib.forms`.
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
Installation & Usage
|
|
63
|
+
--------------------
|
|
64
|
+
|
|
65
|
+
Read the [documentation](https://docs.coderedcorp.com/wagtail-flexible-forms/), which includes a video demo and example project.
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
History & Future of This Project
|
|
69
|
+
--------------------------------
|
|
70
|
+
|
|
71
|
+
This project was [originally developed by Bertrand Bordage of NoriPyt](https://www.kickstarter.com/projects/noripyt/wagtails-first-hatch) with the intention of being merged into Wagtail as `wagtail.contrib.stream_forms`.
|
|
72
|
+
|
|
73
|
+
In 2019, CodeRed used the code as part of Wagtail CRX (`coderedcms`) to and began providing ongoing maintenance as part of that package.
|
|
74
|
+
|
|
75
|
+
In 2025, with renewed interest from the Wagtail community and the Wagtail core team, CodeRed took ownership of the repository and backported the vendored code from `coderedcms` to produce a stable version of `wagtail-flexible-forms` as its own pip package.
|
|
76
|
+
|
|
77
|
+
Stay tuned as we work to add more documentation, examples, and unit tests with the goal of eventually merging the package into Wagtail as the official "stream forms" implementation. It is a fantastic package and we are excited to be investing in it to share with the Wagtail community!
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Wagtail Flexible Forms
|
|
2
|
+
======================
|
|
3
|
+
|
|
4
|
+
Wagtail flexible forms provides a more robust StreamField-based form builder (sometimes called a "StreamForm") with similar functionality to `wagtail.contrib.forms`.
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Installation & Usage
|
|
8
|
+
--------------------
|
|
9
|
+
|
|
10
|
+
Read the [documentation](https://docs.coderedcorp.com/wagtail-flexible-forms/), which includes a video demo and example project.
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
History & Future of This Project
|
|
14
|
+
--------------------------------
|
|
15
|
+
|
|
16
|
+
This project was [originally developed by Bertrand Bordage of NoriPyt](https://www.kickstarter.com/projects/noripyt/wagtails-first-hatch) with the intention of being merged into Wagtail as `wagtail.contrib.stream_forms`.
|
|
17
|
+
|
|
18
|
+
In 2019, CodeRed used the code as part of Wagtail CRX (`coderedcms`) to and began providing ongoing maintenance as part of that package.
|
|
19
|
+
|
|
20
|
+
In 2025, with renewed interest from the Wagtail community and the Wagtail core team, CodeRed took ownership of the repository and backported the vendored code from `coderedcms` to produce a stable version of `wagtail-flexible-forms` as its own pip package.
|
|
21
|
+
|
|
22
|
+
Stay tuned as we work to add more documentation, examples, and unit tests with the goal of eventually merging the package into Wagtail as the official "stream forms" implementation. It is a fantastic package and we are excited to be investing in it to share with the Wagtail community!
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# -- PACKAGE INFO ---------------------
|
|
2
|
+
|
|
3
|
+
[build-system]
|
|
4
|
+
requires = ["setuptools >= 65.0"]
|
|
5
|
+
build-backend = "setuptools.build_meta"
|
|
6
|
+
|
|
7
|
+
[project]
|
|
8
|
+
authors = [
|
|
9
|
+
{name = "CodeRed LLC", email = "info@coderedcorp.com"},
|
|
10
|
+
{name = "Bertrand Bordage"},
|
|
11
|
+
]
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"License :: OSI Approved :: BSD License",
|
|
15
|
+
"Natural Language :: English",
|
|
16
|
+
"Operating System :: OS Independent",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
19
|
+
"Framework :: Django",
|
|
20
|
+
"Framework :: Wagtail",
|
|
21
|
+
"Framework :: Wagtail :: 6",
|
|
22
|
+
"Framework :: Wagtail :: 7",
|
|
23
|
+
"Framework :: Wagtail :: 8",
|
|
24
|
+
]
|
|
25
|
+
dependencies = [
|
|
26
|
+
"wagtail>=6,<8.2",
|
|
27
|
+
]
|
|
28
|
+
description = "Wagtail StreamField-based form builder."
|
|
29
|
+
dynamic = ["version"]
|
|
30
|
+
license = {file = "LICENSE.txt"}
|
|
31
|
+
name = "wagtail-flexible-forms-hg"
|
|
32
|
+
readme = "README.md"
|
|
33
|
+
requires-python = ">=3.9"
|
|
34
|
+
|
|
35
|
+
[project.urls]
|
|
36
|
+
Source = "https://github.com/happygrizzly/wagtail-flexible-forms"
|
|
37
|
+
|
|
38
|
+
[tool.setuptools]
|
|
39
|
+
packages = ["wagtail_flexible_forms"]
|
|
40
|
+
|
|
41
|
+
[tool.setuptools.dynamic]
|
|
42
|
+
version = {attr = "wagtail_flexible_forms.__version__"}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# -- TOOLS ----------------------------
|
|
46
|
+
|
|
47
|
+
[tool.codespell]
|
|
48
|
+
ignore-words-list = ["doubleclick"]
|
|
49
|
+
|
|
50
|
+
[tool.django-stubs]
|
|
51
|
+
django_settings_module = "testproject.settings"
|
|
52
|
+
|
|
53
|
+
[tool.mypy]
|
|
54
|
+
ignore_missing_imports = true
|
|
55
|
+
check_untyped_defs = true
|
|
56
|
+
exclude = [
|
|
57
|
+
'^\..*',
|
|
58
|
+
'migrations',
|
|
59
|
+
'node_modules',
|
|
60
|
+
'venv',
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
[tool.pytest.ini_options]
|
|
64
|
+
DJANGO_SETTINGS_MODULE = "testproject.settings"
|
|
65
|
+
junit_family = "xunit2"
|
|
66
|
+
addopts = "--cov wagtail-flexible-forms --cov-report html --cov-report xml --junitxml junit/test-results.xml"
|
|
67
|
+
python_files = "tests.py test_*.py"
|
|
68
|
+
|
|
69
|
+
[tool.ruff]
|
|
70
|
+
extend-exclude = ["build", "migrations"]
|
|
71
|
+
line-length = 80
|
|
72
|
+
|
|
73
|
+
[tool.ruff.lint]
|
|
74
|
+
extend-select = ["I"]
|
|
75
|
+
|
|
76
|
+
[tool.ruff.lint.isort]
|
|
77
|
+
case-sensitive = false
|
|
78
|
+
force-single-line = true
|
|
79
|
+
lines-after-imports = 2
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.2.0"
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
from anyascii import anyascii
|
|
2
|
+
from django import forms
|
|
3
|
+
from django.db.models import BLANK_CHOICE_DASH
|
|
4
|
+
from django.utils.dateparse import parse_datetime
|
|
5
|
+
from django.utils.text import slugify
|
|
6
|
+
from django.utils.translation import gettext_lazy as _
|
|
7
|
+
from wagtail.blocks import BooleanBlock
|
|
8
|
+
from wagtail.blocks import CharBlock
|
|
9
|
+
from wagtail.blocks import ChoiceBlock
|
|
10
|
+
from wagtail.blocks import DateBlock
|
|
11
|
+
from wagtail.blocks import DateTimeBlock
|
|
12
|
+
from wagtail.blocks import ListBlock
|
|
13
|
+
from wagtail.blocks import RichTextBlock
|
|
14
|
+
from wagtail.blocks import StreamBlock
|
|
15
|
+
from wagtail.blocks import StructBlock
|
|
16
|
+
from wagtail.blocks import TextBlock
|
|
17
|
+
from wagtail.blocks import TimeBlock
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class FormFieldBlock(StructBlock):
|
|
21
|
+
field_label = CharBlock(label=_("Label"))
|
|
22
|
+
help_text = TextBlock(required=False, label=_("Help text"))
|
|
23
|
+
|
|
24
|
+
field_class = forms.CharField
|
|
25
|
+
widget = None
|
|
26
|
+
|
|
27
|
+
def get_slug(self, struct_value):
|
|
28
|
+
return slugify(anyascii(struct_value["field_label"]))
|
|
29
|
+
|
|
30
|
+
def get_field_class(self, struct_value):
|
|
31
|
+
return self.field_class
|
|
32
|
+
|
|
33
|
+
def get_widget(self, struct_value):
|
|
34
|
+
return self.widget
|
|
35
|
+
|
|
36
|
+
def get_field_kwargs(self, struct_value):
|
|
37
|
+
kwargs = {
|
|
38
|
+
"label": struct_value["field_label"],
|
|
39
|
+
"help_text": struct_value["help_text"],
|
|
40
|
+
"required": struct_value.get("required", False),
|
|
41
|
+
}
|
|
42
|
+
if "default_value" in struct_value:
|
|
43
|
+
kwargs["initial"] = struct_value["default_value"]
|
|
44
|
+
form_widget = self.get_widget(struct_value)
|
|
45
|
+
if form_widget is not None:
|
|
46
|
+
kwargs["widget"] = form_widget
|
|
47
|
+
return kwargs
|
|
48
|
+
|
|
49
|
+
def get_field(self, struct_value):
|
|
50
|
+
return self.get_field_class(struct_value)(
|
|
51
|
+
**self.get_field_kwargs(struct_value)
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class OptionalFormFieldBlock(FormFieldBlock):
|
|
56
|
+
required = BooleanBlock(label=_("Required"), required=False)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
CHARFIELD_FORMATS = [
|
|
60
|
+
("email", _("Email")),
|
|
61
|
+
("url", _("URL")),
|
|
62
|
+
]
|
|
63
|
+
try:
|
|
64
|
+
from phonenumber_field.formfields import PhoneNumberField
|
|
65
|
+
except ImportError:
|
|
66
|
+
pass
|
|
67
|
+
else:
|
|
68
|
+
CHARFIELD_FORMATS.append(("phone", _("Phone")))
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class CharFieldBlock(OptionalFormFieldBlock):
|
|
72
|
+
format = ChoiceBlock(
|
|
73
|
+
choices=CHARFIELD_FORMATS, required=False, label=_("Format")
|
|
74
|
+
)
|
|
75
|
+
default_value = CharBlock(required=False, label=_("Default value"))
|
|
76
|
+
|
|
77
|
+
class Meta:
|
|
78
|
+
label = _("Text field (single line)")
|
|
79
|
+
icon = "pilcrow"
|
|
80
|
+
|
|
81
|
+
def get_field_class(self, struct_value):
|
|
82
|
+
text_format = struct_value["format"]
|
|
83
|
+
if text_format == "url":
|
|
84
|
+
return forms.URLField
|
|
85
|
+
if text_format == "email":
|
|
86
|
+
return forms.EmailField
|
|
87
|
+
if text_format == "phone":
|
|
88
|
+
return PhoneNumberField
|
|
89
|
+
return super().get_field_class(struct_value)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class TextFieldBlock(OptionalFormFieldBlock):
|
|
93
|
+
default_value = TextBlock(required=False, label=_("Default value"))
|
|
94
|
+
|
|
95
|
+
widget = forms.Textarea(attrs={"rows": 5})
|
|
96
|
+
|
|
97
|
+
class Meta:
|
|
98
|
+
label = _("Text field (multi line)")
|
|
99
|
+
icon = "pilcrow"
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class NumberFieldBlock(OptionalFormFieldBlock):
|
|
103
|
+
default_value = CharBlock(required=False, label=_("Default value"))
|
|
104
|
+
|
|
105
|
+
widget = forms.NumberInput
|
|
106
|
+
|
|
107
|
+
class Meta:
|
|
108
|
+
label = _("Number field")
|
|
109
|
+
icon = "decimal"
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class CheckboxFieldBlock(FormFieldBlock):
|
|
113
|
+
default_value = BooleanBlock(required=False)
|
|
114
|
+
|
|
115
|
+
field_class = forms.BooleanField
|
|
116
|
+
|
|
117
|
+
class Meta:
|
|
118
|
+
label = _("Checkbox field")
|
|
119
|
+
icon = "tick-inverse"
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class RadioButtonsFieldBlock(OptionalFormFieldBlock):
|
|
123
|
+
choices = ListBlock(CharBlock(label=_("Choice")))
|
|
124
|
+
|
|
125
|
+
field_class = forms.ChoiceField
|
|
126
|
+
widget = forms.RadioSelect
|
|
127
|
+
|
|
128
|
+
class Meta:
|
|
129
|
+
label = _("Radio buttons")
|
|
130
|
+
icon = "radio-empty"
|
|
131
|
+
|
|
132
|
+
def get_field_kwargs(self, struct_value):
|
|
133
|
+
kwargs = super().get_field_kwargs(struct_value)
|
|
134
|
+
kwargs["choices"] = [
|
|
135
|
+
(choice, choice) for choice in struct_value["choices"]
|
|
136
|
+
]
|
|
137
|
+
return kwargs
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class DropdownFieldBlock(RadioButtonsFieldBlock):
|
|
141
|
+
widget = forms.Select
|
|
142
|
+
|
|
143
|
+
class Meta:
|
|
144
|
+
label = _("Dropdown field")
|
|
145
|
+
icon = "list-ul"
|
|
146
|
+
|
|
147
|
+
def get_field_kwargs(self, struct_value):
|
|
148
|
+
kwargs = super(DropdownFieldBlock, self).get_field_kwargs(struct_value)
|
|
149
|
+
kwargs["choices"].insert(0, BLANK_CHOICE_DASH[0])
|
|
150
|
+
return kwargs
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class CheckboxesFieldBlock(OptionalFormFieldBlock):
|
|
154
|
+
checkboxes = ListBlock(CharBlock(label=_("Checkbox")))
|
|
155
|
+
|
|
156
|
+
field_class = forms.MultipleChoiceField
|
|
157
|
+
widget = forms.CheckboxSelectMultiple
|
|
158
|
+
|
|
159
|
+
class Meta:
|
|
160
|
+
label = _("Multiple checkboxes field")
|
|
161
|
+
icon = "tasks"
|
|
162
|
+
|
|
163
|
+
def get_field_kwargs(self, struct_value):
|
|
164
|
+
kwargs = super(CheckboxesFieldBlock, self).get_field_kwargs(
|
|
165
|
+
struct_value
|
|
166
|
+
)
|
|
167
|
+
kwargs["choices"] = [
|
|
168
|
+
(choice, choice) for choice in struct_value["checkboxes"]
|
|
169
|
+
]
|
|
170
|
+
return kwargs
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class DatePickerInput(forms.DateInput):
|
|
174
|
+
def __init__(self, *args, **kwargs):
|
|
175
|
+
attrs = kwargs.get("attrs")
|
|
176
|
+
if attrs is None:
|
|
177
|
+
attrs = {}
|
|
178
|
+
attrs.update(
|
|
179
|
+
{
|
|
180
|
+
"data-provide": "datepicker",
|
|
181
|
+
"data-date-format": "yyyy-mm-dd",
|
|
182
|
+
}
|
|
183
|
+
)
|
|
184
|
+
kwargs["attrs"] = attrs
|
|
185
|
+
super().__init__(*args, **kwargs)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class DateFieldBlock(OptionalFormFieldBlock):
|
|
189
|
+
default_value = DateBlock(required=False)
|
|
190
|
+
|
|
191
|
+
field_class = forms.DateField
|
|
192
|
+
widget = DatePickerInput
|
|
193
|
+
|
|
194
|
+
class Meta:
|
|
195
|
+
label = _("Date field")
|
|
196
|
+
icon = "date"
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
class HTML5TimeInput(forms.TimeInput):
|
|
200
|
+
input_type = "time"
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
class TimeFieldBlock(OptionalFormFieldBlock):
|
|
204
|
+
default_value = TimeBlock(required=False)
|
|
205
|
+
|
|
206
|
+
field_class = forms.TimeField
|
|
207
|
+
widget = HTML5TimeInput
|
|
208
|
+
|
|
209
|
+
class Meta:
|
|
210
|
+
label = _("Time field")
|
|
211
|
+
icon = "time"
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
class DateTimePickerInput(forms.SplitDateTimeWidget):
|
|
215
|
+
def __init__(self, attrs=None, date_format=None, time_format=None):
|
|
216
|
+
super().__init__(
|
|
217
|
+
attrs=attrs, date_format=date_format, time_format=time_format
|
|
218
|
+
)
|
|
219
|
+
self.widgets = (
|
|
220
|
+
DatePickerInput(attrs=attrs, format=date_format),
|
|
221
|
+
HTML5TimeInput(attrs=attrs, format=time_format),
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
def decompress(self, value):
|
|
225
|
+
if isinstance(value, str):
|
|
226
|
+
value = parse_datetime(value)
|
|
227
|
+
return super().decompress(value)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
class DateTimeFieldBlock(OptionalFormFieldBlock):
|
|
231
|
+
default_value = DateTimeBlock(required=False)
|
|
232
|
+
|
|
233
|
+
field_class = forms.SplitDateTimeField
|
|
234
|
+
widget = DateTimePickerInput
|
|
235
|
+
|
|
236
|
+
class Meta:
|
|
237
|
+
label = _("Date+time field")
|
|
238
|
+
icon = "date"
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
class ImageFieldBlock(OptionalFormFieldBlock):
|
|
242
|
+
field_class = forms.ImageField
|
|
243
|
+
|
|
244
|
+
class Meta:
|
|
245
|
+
label = _("Image field")
|
|
246
|
+
icon = "image"
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
class FileFieldBlock(OptionalFormFieldBlock):
|
|
250
|
+
field_class = forms.FileField
|
|
251
|
+
|
|
252
|
+
class Meta:
|
|
253
|
+
label = _("File field")
|
|
254
|
+
icon = "upload"
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
class FormFieldsBlock(StreamBlock):
|
|
258
|
+
char = CharFieldBlock(group=_("Fields"))
|
|
259
|
+
text = TextFieldBlock(group=_("Fields"))
|
|
260
|
+
number = NumberFieldBlock(group=_("Fields"))
|
|
261
|
+
checkbox = CheckboxFieldBlock(group=_("Fields"))
|
|
262
|
+
radios = RadioButtonsFieldBlock(group=_("Fields"))
|
|
263
|
+
dropdown = DropdownFieldBlock(group=_("Fields"))
|
|
264
|
+
checkboxes = CheckboxesFieldBlock(group=_("Fields"))
|
|
265
|
+
date = DateFieldBlock(group=_("Fields"))
|
|
266
|
+
time = TimeFieldBlock(group=_("Fields"))
|
|
267
|
+
datetime = DateTimeFieldBlock(group=_("Fields"))
|
|
268
|
+
image = ImageFieldBlock(group=_("Fields"))
|
|
269
|
+
file = FileFieldBlock(group=_("Fields"))
|
|
270
|
+
text_markup = RichTextBlock(group=_("Other"))
|
|
271
|
+
|
|
272
|
+
class Meta:
|
|
273
|
+
label = _("Form fields")
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
class FormStepBlock(StructBlock):
|
|
277
|
+
name = CharBlock(label=_("Name"), required=False)
|
|
278
|
+
form_fields = FormFieldsBlock()
|
|
279
|
+
|
|
280
|
+
class Meta:
|
|
281
|
+
label = _("Form step")
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
class FormStepsBlock(StreamBlock):
|
|
285
|
+
step = FormStepBlock()
|
|
286
|
+
|
|
287
|
+
class Meta:
|
|
288
|
+
label = _("Form steps")
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from django.template.loader import render_to_string
|
|
2
|
+
from django.utils.safestring import mark_safe
|
|
3
|
+
from django.utils.translation import gettext as _
|
|
4
|
+
from wagtail.admin.panels import EditHandler
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FormSubmissionsPanel(EditHandler):
|
|
8
|
+
template = "wagtailforms/edit_handlers/form_responses_panel.html"
|
|
9
|
+
|
|
10
|
+
def bind_to(self, model=None, instance=None, request=None, form=None):
|
|
11
|
+
new = super().bind_to(model=model)
|
|
12
|
+
if self.heading is None:
|
|
13
|
+
new.heading = _("{} submissions").format(model.get_verbose_name())
|
|
14
|
+
return new
|
|
15
|
+
|
|
16
|
+
def render(self):
|
|
17
|
+
Submission = self.model.get_submission_class()
|
|
18
|
+
submissions = Submission.objects.filter(page=self.instance)
|
|
19
|
+
submission_count = submissions.count()
|
|
20
|
+
|
|
21
|
+
if not submission_count:
|
|
22
|
+
return ""
|
|
23
|
+
|
|
24
|
+
return mark_safe(
|
|
25
|
+
render_to_string(
|
|
26
|
+
self.template,
|
|
27
|
+
{
|
|
28
|
+
"self": self,
|
|
29
|
+
"submission_count": submission_count,
|
|
30
|
+
"last_submit_time": (
|
|
31
|
+
submissions.order_by("submit_time").last().submit_time
|
|
32
|
+
),
|
|
33
|
+
},
|
|
34
|
+
)
|
|
35
|
+
)
|