wagtailstreamforms 0.0.0__py3-none-any.whl
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.
- wagtailstreamforms/__init__.py +1 -0
- wagtailstreamforms/blocks.py +93 -0
- wagtailstreamforms/conf.py +35 -0
- wagtailstreamforms/fields.py +218 -0
- wagtailstreamforms/forms.py +61 -0
- wagtailstreamforms/hooks.py +62 -0
- wagtailstreamforms/locale/es/LC_MESSAGES/django.mo +0 -0
- wagtailstreamforms/locale/es/LC_MESSAGES/django.po +328 -0
- wagtailstreamforms/locale/nl/LC_MESSAGES/django.mo +0 -0
- wagtailstreamforms/locale/nl/LC_MESSAGES/django.po +464 -0
- wagtailstreamforms/locale/tr/LC_MESSAGES/django.mo +0 -0
- wagtailstreamforms/locale/tr/LC_MESSAGES/django.po +317 -0
- wagtailstreamforms/management/__init__.py +0 -0
- wagtailstreamforms/management/commands/__init__.py +0 -0
- wagtailstreamforms/management/commands/prunesubmissions.py +29 -0
- wagtailstreamforms/migrations/0001_initial.py +555 -0
- wagtailstreamforms/migrations/0002_form_site.py +21 -0
- wagtailstreamforms/migrations/0003_alter_form_fields.py +20 -0
- wagtailstreamforms/migrations/__init__.py +0 -0
- wagtailstreamforms/models/__init__.py +39 -0
- wagtailstreamforms/models/abstract.py +15 -0
- wagtailstreamforms/models/file.py +35 -0
- wagtailstreamforms/models/form.py +188 -0
- wagtailstreamforms/models/submission.py +27 -0
- wagtailstreamforms/serializers.py +11 -0
- wagtailstreamforms/streamfield.py +48 -0
- wagtailstreamforms/templates/streamforms/advanced_settings.html +25 -0
- wagtailstreamforms/templates/streamforms/confirm_copy.html +26 -0
- wagtailstreamforms/templates/streamforms/confirm_delete.html +23 -0
- wagtailstreamforms/templates/streamforms/form_block.html +10 -0
- wagtailstreamforms/templates/streamforms/index_submissions.html +104 -0
- wagtailstreamforms/templates/streamforms/list_submissions.html +56 -0
- wagtailstreamforms/templates/streamforms/non_existent_form.html +2 -0
- wagtailstreamforms/templates/streamforms/partials/form_field.html +6 -0
- wagtailstreamforms/templates/streamforms/partials/pagination_nav.html +17 -0
- wagtailstreamforms/templates/streamforms/wagtailadmin/shared/datetimepicker_translations.html +39 -0
- wagtailstreamforms/templatetags/__init__.py +0 -0
- wagtailstreamforms/templatetags/streamforms_tags.py +43 -0
- wagtailstreamforms/urls.py +31 -0
- wagtailstreamforms/utils/__init__.py +0 -0
- wagtailstreamforms/utils/apps.py +25 -0
- wagtailstreamforms/utils/general.py +6 -0
- wagtailstreamforms/utils/loading.py +30 -0
- wagtailstreamforms/utils/requests.py +13 -0
- wagtailstreamforms/utils/version.py +0 -0
- wagtailstreamforms/views/__init__.py +4 -0
- wagtailstreamforms/views/advanced_settings.py +51 -0
- wagtailstreamforms/views/copy.py +83 -0
- wagtailstreamforms/views/submission_delete.py +73 -0
- wagtailstreamforms/views/submission_list.py +112 -0
- wagtailstreamforms/wagtail_hooks.py +255 -0
- wagtailstreamforms/wagtailstreamforms_fields.py +274 -0
- wagtailstreamforms/wagtailstreamforms_hooks.py +31 -0
- wagtailstreamforms-0.0.0.dist-info/METADATA +24 -0
- wagtailstreamforms-0.0.0.dist-info/RECORD +58 -0
- wagtailstreamforms-0.0.0.dist-info/WHEEL +5 -0
- wagtailstreamforms-0.0.0.dist-info/licenses/LICENSE +19 -0
- wagtailstreamforms-0.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from django.db import models, transaction
|
|
2
|
+
from django.db.models.signals import post_delete
|
|
3
|
+
from django.utils.translation import gettext_lazy as _
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class FormSubmissionFile(models.Model):
|
|
7
|
+
"""Data for a form submission file."""
|
|
8
|
+
|
|
9
|
+
submission = models.ForeignKey(
|
|
10
|
+
"FormSubmission",
|
|
11
|
+
verbose_name=_("Submission"),
|
|
12
|
+
on_delete=models.CASCADE,
|
|
13
|
+
related_name="files",
|
|
14
|
+
)
|
|
15
|
+
field = models.CharField(verbose_name=_("Field"), max_length=255)
|
|
16
|
+
file = models.FileField(verbose_name=_("File"), upload_to="streamforms/")
|
|
17
|
+
|
|
18
|
+
def __str__(self):
|
|
19
|
+
return self.file.name
|
|
20
|
+
|
|
21
|
+
class Meta:
|
|
22
|
+
ordering = ["field", "file"]
|
|
23
|
+
verbose_name = _("Form submission file")
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def url(self):
|
|
27
|
+
return self.file.url
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def delete_file_from_storage(instance, **kwargs):
|
|
31
|
+
"""Cleanup deleted files from disk"""
|
|
32
|
+
transaction.on_commit(lambda: instance.file.delete(False))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
post_delete.connect(delete_file_from_storage, sender=FormSubmissionFile)
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
|
|
3
|
+
from django.conf import settings
|
|
4
|
+
from django.db import models
|
|
5
|
+
from django.utils.translation import gettext_lazy as _
|
|
6
|
+
from wagtail import VERSION as WAGTAIL_VERSION
|
|
7
|
+
from wagtail.admin.panels import (
|
|
8
|
+
FieldPanel,
|
|
9
|
+
MultiFieldPanel,
|
|
10
|
+
ObjectList,
|
|
11
|
+
PageChooserPanel,
|
|
12
|
+
TabbedInterface,
|
|
13
|
+
)
|
|
14
|
+
from wagtail.models import Site
|
|
15
|
+
|
|
16
|
+
from wagtailstreamforms import hooks
|
|
17
|
+
from wagtailstreamforms.conf import get_setting
|
|
18
|
+
from wagtailstreamforms.fields import HookSelectField
|
|
19
|
+
from wagtailstreamforms.forms import FormBuilder
|
|
20
|
+
from wagtailstreamforms.streamfield import FormFieldsStreamField
|
|
21
|
+
from wagtailstreamforms.utils.general import get_slug_from_string
|
|
22
|
+
from wagtailstreamforms.utils.loading import get_advanced_settings_model
|
|
23
|
+
|
|
24
|
+
from .submission import FormSubmission
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class FormQuerySet(models.QuerySet):
|
|
28
|
+
def for_site(self, site):
|
|
29
|
+
"""Return all forms for a specific site."""
|
|
30
|
+
return self.filter(site=site)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class AbstractForm(models.Model):
|
|
34
|
+
site = models.ForeignKey(Site, on_delete=models.SET_NULL, null=True, blank=True)
|
|
35
|
+
title = models.CharField(_("Title"), max_length=255)
|
|
36
|
+
slug = models.SlugField(
|
|
37
|
+
_("Slug"),
|
|
38
|
+
allow_unicode=True,
|
|
39
|
+
max_length=255,
|
|
40
|
+
unique=True,
|
|
41
|
+
help_text=_("Used to identify the form in template tags"),
|
|
42
|
+
)
|
|
43
|
+
template_name = models.CharField(
|
|
44
|
+
_("Template"), max_length=255, choices=get_setting("FORM_TEMPLATES")
|
|
45
|
+
)
|
|
46
|
+
fields = FormFieldsStreamField([], use_json_field=True, verbose_name=_("Fields"))
|
|
47
|
+
submit_button_text = models.CharField(
|
|
48
|
+
_("Submit button text"), max_length=100, default="Submit"
|
|
49
|
+
)
|
|
50
|
+
success_message = models.CharField(
|
|
51
|
+
_("Success message"),
|
|
52
|
+
blank=True,
|
|
53
|
+
max_length=255,
|
|
54
|
+
help_text=_(
|
|
55
|
+
"An optional success message to show when the form has been successfully submitted"
|
|
56
|
+
),
|
|
57
|
+
)
|
|
58
|
+
error_message = models.CharField(
|
|
59
|
+
_("Error message"),
|
|
60
|
+
blank=True,
|
|
61
|
+
max_length=255,
|
|
62
|
+
help_text=_("An optional error message to show when the form has validation errors"),
|
|
63
|
+
)
|
|
64
|
+
post_redirect_page = models.ForeignKey(
|
|
65
|
+
"wagtailcore.Page",
|
|
66
|
+
verbose_name=_("Post redirect page"),
|
|
67
|
+
on_delete=models.SET_NULL,
|
|
68
|
+
null=True,
|
|
69
|
+
blank=True,
|
|
70
|
+
related_name="+",
|
|
71
|
+
help_text=_("The page to redirect to after a successful submission"),
|
|
72
|
+
)
|
|
73
|
+
process_form_submission_hooks = HookSelectField(verbose_name=_("Submission hooks"), blank=True)
|
|
74
|
+
|
|
75
|
+
objects = FormQuerySet.as_manager()
|
|
76
|
+
|
|
77
|
+
settings_panels = [
|
|
78
|
+
FieldPanel("title", classname="full"),
|
|
79
|
+
FieldPanel("slug"),
|
|
80
|
+
FieldPanel("template_name"),
|
|
81
|
+
FieldPanel("submit_button_text"),
|
|
82
|
+
MultiFieldPanel(
|
|
83
|
+
[FieldPanel("success_message"), FieldPanel("error_message")], _("Messages")
|
|
84
|
+
),
|
|
85
|
+
FieldPanel("process_form_submission_hooks", classname="choice_field"),
|
|
86
|
+
PageChooserPanel("post_redirect_page"),
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
field_panels = [FieldPanel("fields")]
|
|
90
|
+
|
|
91
|
+
edit_handler = TabbedInterface(
|
|
92
|
+
[
|
|
93
|
+
ObjectList(settings_panels, heading=_("General")),
|
|
94
|
+
ObjectList(field_panels, heading=_("Fields")),
|
|
95
|
+
]
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
def __str__(self):
|
|
99
|
+
return self.title
|
|
100
|
+
|
|
101
|
+
class Meta:
|
|
102
|
+
abstract = True
|
|
103
|
+
ordering = ["title"]
|
|
104
|
+
verbose_name = _("Form")
|
|
105
|
+
verbose_name_plural = _("Forms")
|
|
106
|
+
|
|
107
|
+
def copy(self):
|
|
108
|
+
"""Copy this form and its fields."""
|
|
109
|
+
|
|
110
|
+
form_copy = Form(
|
|
111
|
+
site=self.site,
|
|
112
|
+
title=self.title,
|
|
113
|
+
slug=uuid.uuid4(),
|
|
114
|
+
template_name=self.template_name,
|
|
115
|
+
fields=self.fields,
|
|
116
|
+
submit_button_text=self.submit_button_text,
|
|
117
|
+
success_message=self.success_message,
|
|
118
|
+
error_message=self.error_message,
|
|
119
|
+
post_redirect_page=self.post_redirect_page,
|
|
120
|
+
process_form_submission_hooks=self.process_form_submission_hooks,
|
|
121
|
+
)
|
|
122
|
+
form_copy.save()
|
|
123
|
+
|
|
124
|
+
# additionally copy the advanced settings if they exist
|
|
125
|
+
SettingsModel = get_advanced_settings_model()
|
|
126
|
+
|
|
127
|
+
if SettingsModel:
|
|
128
|
+
try:
|
|
129
|
+
advanced = SettingsModel.objects.get(form=self)
|
|
130
|
+
advanced.pk = None
|
|
131
|
+
advanced.form = form_copy
|
|
132
|
+
advanced.save()
|
|
133
|
+
except SettingsModel.DoesNotExist:
|
|
134
|
+
pass
|
|
135
|
+
|
|
136
|
+
return form_copy
|
|
137
|
+
|
|
138
|
+
copy.alters_data = True
|
|
139
|
+
|
|
140
|
+
def get_data_fields(self):
|
|
141
|
+
"""Returns a list of tuples with (field_name, field_label)."""
|
|
142
|
+
|
|
143
|
+
data_fields = [("submit_time", _("Submission date"))]
|
|
144
|
+
data_fields += [
|
|
145
|
+
(get_slug_from_string(field["value"]["label"]), field["value"]["label"])
|
|
146
|
+
for field in self.get_form_fields()
|
|
147
|
+
]
|
|
148
|
+
if getattr(settings, "WAGTAILSTREAMFORMS_SHOW_FORM_REFERENCE", False):
|
|
149
|
+
data_fields += [("form_reference", _("Form reference"))]
|
|
150
|
+
return data_fields
|
|
151
|
+
|
|
152
|
+
def get_form(self, *args, **kwargs):
|
|
153
|
+
"""Returns the form."""
|
|
154
|
+
|
|
155
|
+
form_class = self.get_form_class()
|
|
156
|
+
return form_class(*args, **kwargs)
|
|
157
|
+
|
|
158
|
+
def get_form_class(self):
|
|
159
|
+
"""Returns the form class."""
|
|
160
|
+
|
|
161
|
+
return FormBuilder(self.get_form_fields()).get_form_class()
|
|
162
|
+
|
|
163
|
+
def get_form_fields(self):
|
|
164
|
+
"""Returns the form field's stream data."""
|
|
165
|
+
|
|
166
|
+
if WAGTAIL_VERSION >= (2, 12):
|
|
167
|
+
form_fields = self.fields.raw_data
|
|
168
|
+
else:
|
|
169
|
+
form_fields = self.fields.stream_data
|
|
170
|
+
for fn in hooks.get_hooks("construct_submission_form_fields"):
|
|
171
|
+
form_fields = fn(form_fields)
|
|
172
|
+
return form_fields
|
|
173
|
+
|
|
174
|
+
def get_submission_class(self):
|
|
175
|
+
"""Returns submission class."""
|
|
176
|
+
|
|
177
|
+
return FormSubmission
|
|
178
|
+
|
|
179
|
+
def process_form_submission(self, form):
|
|
180
|
+
"""Runs each hook if selected in the form."""
|
|
181
|
+
|
|
182
|
+
for fn in hooks.get_hooks("process_form_submission"):
|
|
183
|
+
if fn.__name__ in self.process_form_submission_hooks:
|
|
184
|
+
fn(self, form)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class Form(AbstractForm):
|
|
188
|
+
pass
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
from django.db import models
|
|
4
|
+
from django.utils.translation import gettext_lazy as _
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FormSubmission(models.Model):
|
|
8
|
+
"""Data for a form submission."""
|
|
9
|
+
|
|
10
|
+
form_data = models.TextField(_("Form data"))
|
|
11
|
+
form = models.ForeignKey("Form", verbose_name=_("Form"), on_delete=models.CASCADE)
|
|
12
|
+
submit_time = models.DateTimeField(_("Submit time"), auto_now_add=True)
|
|
13
|
+
|
|
14
|
+
def get_data(self):
|
|
15
|
+
"""Returns dict with form data."""
|
|
16
|
+
form_data = json.loads(self.form_data)
|
|
17
|
+
|
|
18
|
+
form_data.update({"submit_time": self.submit_time})
|
|
19
|
+
|
|
20
|
+
return form_data
|
|
21
|
+
|
|
22
|
+
def __str__(self):
|
|
23
|
+
return self.form_data
|
|
24
|
+
|
|
25
|
+
class Meta:
|
|
26
|
+
ordering = ["-submit_time"]
|
|
27
|
+
verbose_name = _("Form submission")
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from django.core.serializers.json import DjangoJSONEncoder
|
|
2
|
+
from django.db import models
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class FormSubmissionSerializer(DjangoJSONEncoder):
|
|
6
|
+
"""Form submission serializer"""
|
|
7
|
+
|
|
8
|
+
def default(self, o):
|
|
9
|
+
if isinstance(o, models.Model):
|
|
10
|
+
return str(o)
|
|
11
|
+
return super().default(o)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from django.core.exceptions import ImproperlyConfigured
|
|
2
|
+
from wagtail import blocks
|
|
3
|
+
from wagtail.fields import StreamField
|
|
4
|
+
|
|
5
|
+
from wagtailstreamforms.fields import BaseField, get_fields
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class FormFieldStreamBlock(blocks.StreamBlock):
|
|
9
|
+
"""Add all registered instances of BaseField's get_form_block method to the streamfield."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, local_blocks=None, **kwargs) -> None:
|
|
12
|
+
self._constructor_kwargs = kwargs
|
|
13
|
+
|
|
14
|
+
# Note, this is calling BaseStreamBlock's super __init__, not FormFieldStreamBlock's.
|
|
15
|
+
# We don't want BaseStreamBlock.__init__() to run, because it tries to assign to
|
|
16
|
+
# self.child_blocks, which we've overridden with a @property. But we DO want
|
|
17
|
+
# Block.__init__() to run.
|
|
18
|
+
super(blocks.BaseStreamBlock, self).__init__()
|
|
19
|
+
|
|
20
|
+
self._child_blocks = self.base_blocks.copy()
|
|
21
|
+
|
|
22
|
+
for name, field_class in get_fields().items():
|
|
23
|
+
# ensure the field is a subclass of BaseField.
|
|
24
|
+
if not issubclass(field_class, BaseField):
|
|
25
|
+
raise ImproperlyConfigured(
|
|
26
|
+
"'%s' must be a subclass of '%s'" % (field_class, BaseField)
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# assign the block
|
|
30
|
+
block = field_class().get_form_block()
|
|
31
|
+
block.set_name(name)
|
|
32
|
+
self._child_blocks[name] = block
|
|
33
|
+
|
|
34
|
+
self._dependencies = self._child_blocks.values()
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def child_blocks(self):
|
|
38
|
+
return self._child_blocks
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def dependencies(self):
|
|
42
|
+
return self._dependencies
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class FormFieldsStreamField(StreamField):
|
|
46
|
+
def __init__(self, block_types, **kwargs) -> None:
|
|
47
|
+
super().__init__(block_types, **kwargs)
|
|
48
|
+
self.stream_block = FormFieldStreamBlock(block_types, required=not self.blank)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{% extends "wagtailadmin/base.html" %}
|
|
2
|
+
{% load i18n %}
|
|
3
|
+
{% block titletag %}{% blocktrans with form_title=object %}Advanced form settings for {{ form_title }}{% endblocktrans %}{% endblock %}
|
|
4
|
+
{% block bodyclass %}menu-explorer{% endblock %}
|
|
5
|
+
|
|
6
|
+
{% block content %}
|
|
7
|
+
{% trans "Advanced form settings" as title_str %}
|
|
8
|
+
{% include "wagtailadmin/shared/header.html" with title=title_str subtitle=object icon="doc-empty-inverse" %}
|
|
9
|
+
|
|
10
|
+
<div class="nice-padding">
|
|
11
|
+
<form action="." method="POST" novalidate>
|
|
12
|
+
{% csrf_token %}
|
|
13
|
+
<ul class="fields">
|
|
14
|
+
{% block visible_fields %}
|
|
15
|
+
{% for field in form.visible_fields %}
|
|
16
|
+
{% include "wagtailadmin/shared/field.html" %}
|
|
17
|
+
{% endfor %}
|
|
18
|
+
{% endblock %}
|
|
19
|
+
<li>
|
|
20
|
+
<input type="submit" value="{% trans 'Save' %}" class="button" />
|
|
21
|
+
</li>
|
|
22
|
+
</ul>
|
|
23
|
+
</form>
|
|
24
|
+
</div>
|
|
25
|
+
{% endblock %}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{% extends "wagtailadmin/base.html" %}
|
|
2
|
+
{% load i18n %}
|
|
3
|
+
{% block titletag %}{% blocktrans with form_title=object.title|capfirst %}Copy of {{ form_title }}{% endblocktrans %}{% endblock %}
|
|
4
|
+
{% block bodyclass %}menu-explorer{% endblock %}
|
|
5
|
+
|
|
6
|
+
{% block content %}
|
|
7
|
+
{% trans "Copy" as copy_str %}
|
|
8
|
+
{% include "wagtailadmin/shared/header.html" with title=copy_str subtitle=object.title icon="doc-empty-inverse" %}
|
|
9
|
+
|
|
10
|
+
<div class="nice-padding">
|
|
11
|
+
<p>{% trans 'Are you sure you want to copy this form?' %}</p>
|
|
12
|
+
<form action="." method="POST" novalidate>
|
|
13
|
+
{% csrf_token %}
|
|
14
|
+
<ul class="fields">
|
|
15
|
+
{% block visible_fields %}
|
|
16
|
+
{% for field in form.visible_fields %}
|
|
17
|
+
{% include "wagtailadmin/shared/field.html" %}
|
|
18
|
+
{% endfor %}
|
|
19
|
+
{% endblock %}
|
|
20
|
+
<li>
|
|
21
|
+
<input type="submit" value="{% trans 'Copy' %}" class="button" />
|
|
22
|
+
</li>
|
|
23
|
+
</ul>
|
|
24
|
+
</form>
|
|
25
|
+
</div>
|
|
26
|
+
{% endblock %}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{% extends "wagtailadmin/base.html" %}.
|
|
2
|
+
{% load i18n %}
|
|
3
|
+
{% block titletag %}{% blocktrans with form_title=object.title|capfirst %}Submissions of {{ form_title }}{% endblocktrans %}{% endblock %}
|
|
4
|
+
{% block bodyclass %}menu-explorer{% endblock %}
|
|
5
|
+
|
|
6
|
+
{% block content %}
|
|
7
|
+
{% trans "Delete form data" as del_str %}
|
|
8
|
+
{% include "wagtailadmin/shared/header.html" with title=del_str subtitle=object.title icon="doc-empty-inverse" %}
|
|
9
|
+
|
|
10
|
+
<div class="nice-padding">
|
|
11
|
+
<p>
|
|
12
|
+
{% blocktrans count counter=submissions.count %}
|
|
13
|
+
Are you sure you want to delete this form submission?
|
|
14
|
+
{% plural %}
|
|
15
|
+
Are you sure you want to delete these form submissions?
|
|
16
|
+
{% endblocktrans %}
|
|
17
|
+
</p>
|
|
18
|
+
<form action="{% url 'wagtailstreamforms:streamforms_delete_submissions' object.id %}?{{ request.GET.urlencode }}" method="POST">
|
|
19
|
+
{% csrf_token %}
|
|
20
|
+
<input type="submit" value="{% trans 'Delete' %}" class="button serious">
|
|
21
|
+
</form>
|
|
22
|
+
</div>
|
|
23
|
+
{% endblock %}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<h2>{{ value.form.title }}</h2>
|
|
2
|
+
<form{% if form.is_multipart %} enctype="multipart/form-data"{% endif %} action="{{ value.form_action }}" method="post" novalidate>
|
|
3
|
+
{{ form.media }}
|
|
4
|
+
{% csrf_token %}
|
|
5
|
+
{% for hidden in form.hidden_fields %}{{ hidden }}{% endfor %}
|
|
6
|
+
{% for field in form.visible_fields %}
|
|
7
|
+
{% include 'streamforms/partials/form_field.html' %}
|
|
8
|
+
{% endfor %}
|
|
9
|
+
<input type="submit" value="{{ value.form.submit_button_text }}">
|
|
10
|
+
</form>
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
{% extends "wagtailadmin/base.html" %}
|
|
2
|
+
{% load i18n %}
|
|
3
|
+
{% block titletag %}{% blocktrans with form_title=object.title|capfirst %}Submissions of {{ form_title }}{% endblocktrans %}{% endblock %}
|
|
4
|
+
{% block extra_js %}
|
|
5
|
+
{{ block.super }}
|
|
6
|
+
{% include "streamforms/wagtailadmin/shared/datetimepicker_translations.html" %}
|
|
7
|
+
|
|
8
|
+
<script>
|
|
9
|
+
$(function() {
|
|
10
|
+
$('#id_date_from').datetimepicker({
|
|
11
|
+
timepicker: false,
|
|
12
|
+
format: 'Y-m-d',
|
|
13
|
+
});
|
|
14
|
+
$('#id_date_to').datetimepicker({
|
|
15
|
+
timepicker: false,
|
|
16
|
+
format: 'Y-m-d',
|
|
17
|
+
});
|
|
18
|
+
var selectAllCheckbox = document.getElementById('select-all');
|
|
19
|
+
var deleteButton = document.getElementById('delete-submissions');
|
|
20
|
+
var selectedSubmissions = {};
|
|
21
|
+
function updateActions() {
|
|
22
|
+
var submissionCheckboxes = $('input[type=checkbox].select-submission');
|
|
23
|
+
var someSubmissionsSelected = submissionCheckboxes.is(':checked');
|
|
24
|
+
var everySubmissionSelected = !submissionCheckboxes.is(':not(:checked)');
|
|
25
|
+
// Select all box state
|
|
26
|
+
if (everySubmissionSelected) {
|
|
27
|
+
// Every submission has been selected
|
|
28
|
+
selectAllCheckbox.checked = true;
|
|
29
|
+
selectAllCheckbox.indeterminate = false;
|
|
30
|
+
} else if (someSubmissionsSelected) {
|
|
31
|
+
// At least one, but not all submissions have been selected
|
|
32
|
+
selectAllCheckbox.checked = false;
|
|
33
|
+
selectAllCheckbox.indeterminate = true;
|
|
34
|
+
} else {
|
|
35
|
+
// No submissions have been selected
|
|
36
|
+
selectAllCheckbox.checked = false;
|
|
37
|
+
selectAllCheckbox.indeterminate = false;
|
|
38
|
+
}
|
|
39
|
+
// Delete button state
|
|
40
|
+
if (someSubmissionsSelected) {
|
|
41
|
+
deleteButton.classList.remove('disabled')
|
|
42
|
+
deleteButton.style.visibility = "visible";
|
|
43
|
+
} else {
|
|
44
|
+
deleteButton.classList.add('disabled')
|
|
45
|
+
deleteButton.style.visibility = "hidden";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// Event handlers
|
|
49
|
+
$(selectAllCheckbox).on('change', function() {
|
|
50
|
+
let checked = this.checked;
|
|
51
|
+
// Update checkbox states
|
|
52
|
+
$('input[type=checkbox].select-submission').each(function() {
|
|
53
|
+
this.checked = checked;
|
|
54
|
+
});
|
|
55
|
+
updateActions();
|
|
56
|
+
});
|
|
57
|
+
$('input[type=checkbox].select-submission').on('change', function() {
|
|
58
|
+
updateActions();
|
|
59
|
+
});
|
|
60
|
+
// initial call to updateActions to bring delete button state in sync with checkboxes
|
|
61
|
+
// in the case that some checkboxes are pre-checked (which will be the case in some
|
|
62
|
+
// browsers when using the back button)
|
|
63
|
+
$(document).ready(function() { updateActions() });
|
|
64
|
+
});
|
|
65
|
+
</script>
|
|
66
|
+
{% endblock %}
|
|
67
|
+
{% block content %}
|
|
68
|
+
<header class="nice-padding hasform">
|
|
69
|
+
<form action="" method="get" novalidate>
|
|
70
|
+
<div class="row">
|
|
71
|
+
<div class="left">
|
|
72
|
+
<div class="col">
|
|
73
|
+
<h1 class="icon icon-form">
|
|
74
|
+
{% blocktrans with form_title=object.title|capfirst %}Form data <span>{{ form_title }}</span>{% endblocktrans %}
|
|
75
|
+
</h1>
|
|
76
|
+
</div>
|
|
77
|
+
<div class="col search-bar">
|
|
78
|
+
<ul class="fields row rowflush">
|
|
79
|
+
{% for field in filter_form %}
|
|
80
|
+
{% include "wagtailadmin/shared/field.html" with field=field field_classes="field-small" li_classes="col4" %}
|
|
81
|
+
{% endfor %}
|
|
82
|
+
<li class="submit col2">
|
|
83
|
+
<button name="action" value="filter" class="button button-filter">{% trans 'Filter' %}</button>
|
|
84
|
+
</li>
|
|
85
|
+
</ul>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
<div class="right">
|
|
89
|
+
<button name="action" value="CSV" class="button bicolor icon icon-download">{% trans 'Download CSV' %}</button>
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
</form>
|
|
93
|
+
</header>
|
|
94
|
+
<div>
|
|
95
|
+
{% if object_list %}
|
|
96
|
+
<form action="{% url 'wagtailstreamforms:streamforms_delete_submissions' object.id %}" method="get">
|
|
97
|
+
{% include "streamforms/list_submissions.html" %}
|
|
98
|
+
{% include "streamforms/partials/pagination_nav.html" with items=page_obj %}
|
|
99
|
+
</form>
|
|
100
|
+
{% else %}
|
|
101
|
+
<p class="no-results-message nice-padding">{% blocktrans with title=object.title %}There have been no submissions of the '{{ title }}' form.{% endblocktrans %}</p>
|
|
102
|
+
{% endif %}
|
|
103
|
+
</div>
|
|
104
|
+
{% endblock %}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{% load i18n %}
|
|
2
|
+
<div class="overflow">
|
|
3
|
+
<table class="listing">
|
|
4
|
+
<col />
|
|
5
|
+
<col />
|
|
6
|
+
<col />
|
|
7
|
+
<thead>
|
|
8
|
+
{% if has_delete_permission %}
|
|
9
|
+
<tr>
|
|
10
|
+
<th colspan="{{ data_headings|length|add:1 }}">
|
|
11
|
+
<button class="button no" id="delete-submissions" style="visibility: hidden">{% trans "Delete selected submissions" %}</button>
|
|
12
|
+
</th>
|
|
13
|
+
</tr>
|
|
14
|
+
{% endif %}
|
|
15
|
+
<tr>
|
|
16
|
+
{% if has_delete_permission %}
|
|
17
|
+
<th><input type="checkbox" id="select-all" /></th>
|
|
18
|
+
{% endif %}
|
|
19
|
+
{% for heading in data_headings %}
|
|
20
|
+
<th>{{ heading }}</th>
|
|
21
|
+
{% endfor %}
|
|
22
|
+
</tr>
|
|
23
|
+
</thead>
|
|
24
|
+
<tbody>
|
|
25
|
+
{% for row in data_rows %}
|
|
26
|
+
<tr>
|
|
27
|
+
{% if has_delete_permission %}
|
|
28
|
+
<td>
|
|
29
|
+
<input type="checkbox" name="selected-submissions" class="select-submission" value="{{ row.model_id }}" />
|
|
30
|
+
</td>
|
|
31
|
+
{% endif %}
|
|
32
|
+
{% for cell in row.fields %}
|
|
33
|
+
<td>
|
|
34
|
+
{{ cell }}
|
|
35
|
+
</td>
|
|
36
|
+
{% endfor %}
|
|
37
|
+
<td>
|
|
38
|
+
</tr>
|
|
39
|
+
{% if row.files %}
|
|
40
|
+
<tr>
|
|
41
|
+
<td>{% trans 'Files' %}</td>
|
|
42
|
+
<td colspan="{{ row.fields|length }}">
|
|
43
|
+
<ul>
|
|
44
|
+
{% for file in row.files %}
|
|
45
|
+
<li style="display: inline; margin-right: 20px;">
|
|
46
|
+
<a href="{{ file.url }}" target="_blank">{{ file }}</a>
|
|
47
|
+
</li>
|
|
48
|
+
{% endfor %}
|
|
49
|
+
</ul>
|
|
50
|
+
</td>
|
|
51
|
+
</tr>
|
|
52
|
+
{% endif %}
|
|
53
|
+
{% endfor %}
|
|
54
|
+
</tbody>
|
|
55
|
+
</table>
|
|
56
|
+
</div>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{% load i18n streamforms_tags %}
|
|
2
|
+
|
|
3
|
+
<div class="pagination">
|
|
4
|
+
<p>{% blocktrans with page_num=items.number total_pages=items.paginator.num_pages %}Page {{ page_num }} of {{ total_pages }}.{% endblocktrans %}</p>
|
|
5
|
+
<ul>
|
|
6
|
+
<li class="prev">
|
|
7
|
+
{% if items.has_previous %}
|
|
8
|
+
<a href="?{% url_replace p=items.previous_page_number %}" class="icon icon-arrow-left">{% trans 'Previous' %}</a>
|
|
9
|
+
{% endif %}
|
|
10
|
+
</li>
|
|
11
|
+
<li class="next">
|
|
12
|
+
{% if items.has_next %}
|
|
13
|
+
<a href="?{% url_replace p=items.next_page_number %}" class="icon icon-arrow-right-after">{% trans 'Next' %}</a>
|
|
14
|
+
{% endif %}
|
|
15
|
+
</li>
|
|
16
|
+
</ul>
|
|
17
|
+
</div>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{% load i18n %}
|
|
2
|
+
|
|
3
|
+
<script>
|
|
4
|
+
$.fn.datetimepicker.defaults.i18n.wagtail_custom_locale = {
|
|
5
|
+
months: [
|
|
6
|
+
'{% trans "January" %}',
|
|
7
|
+
'{% trans "February" %}',
|
|
8
|
+
'{% trans "March" %}',
|
|
9
|
+
'{% trans "April" %}',
|
|
10
|
+
'{% trans "May" %}',
|
|
11
|
+
'{% trans "June" %}',
|
|
12
|
+
'{% trans "July" %}',
|
|
13
|
+
'{% trans "August" %}',
|
|
14
|
+
'{% trans "September" %}',
|
|
15
|
+
'{% trans "October" %}',
|
|
16
|
+
'{% trans "November" %}',
|
|
17
|
+
'{% trans "December" %}'
|
|
18
|
+
],
|
|
19
|
+
dayOfWeek: [
|
|
20
|
+
'{% trans "Sunday" %}',
|
|
21
|
+
'{% trans "Monday" %}',
|
|
22
|
+
'{% trans "Tuesday" %}',
|
|
23
|
+
'{% trans "Wednesday" %}',
|
|
24
|
+
'{% trans "Thursday" %}',
|
|
25
|
+
'{% trans "Friday" %}',
|
|
26
|
+
'{% trans "Saturday" %}'
|
|
27
|
+
],
|
|
28
|
+
dayOfWeekShort: [
|
|
29
|
+
'{% trans "Sun" %}',
|
|
30
|
+
'{% trans "Mon" %}',
|
|
31
|
+
'{% trans "Tue" %}',
|
|
32
|
+
'{% trans "Wed" %}',
|
|
33
|
+
'{% trans "Thu" %}',
|
|
34
|
+
'{% trans "Fri" %}',
|
|
35
|
+
'{% trans "Sat" %}'
|
|
36
|
+
],
|
|
37
|
+
}
|
|
38
|
+
$.datetimepicker.setLocale('wagtail_custom_locale');
|
|
39
|
+
</script>
|
|
File without changes
|