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.
Files changed (58) hide show
  1. wagtailstreamforms/__init__.py +1 -0
  2. wagtailstreamforms/blocks.py +93 -0
  3. wagtailstreamforms/conf.py +35 -0
  4. wagtailstreamforms/fields.py +218 -0
  5. wagtailstreamforms/forms.py +61 -0
  6. wagtailstreamforms/hooks.py +62 -0
  7. wagtailstreamforms/locale/es/LC_MESSAGES/django.mo +0 -0
  8. wagtailstreamforms/locale/es/LC_MESSAGES/django.po +328 -0
  9. wagtailstreamforms/locale/nl/LC_MESSAGES/django.mo +0 -0
  10. wagtailstreamforms/locale/nl/LC_MESSAGES/django.po +464 -0
  11. wagtailstreamforms/locale/tr/LC_MESSAGES/django.mo +0 -0
  12. wagtailstreamforms/locale/tr/LC_MESSAGES/django.po +317 -0
  13. wagtailstreamforms/management/__init__.py +0 -0
  14. wagtailstreamforms/management/commands/__init__.py +0 -0
  15. wagtailstreamforms/management/commands/prunesubmissions.py +29 -0
  16. wagtailstreamforms/migrations/0001_initial.py +555 -0
  17. wagtailstreamforms/migrations/0002_form_site.py +21 -0
  18. wagtailstreamforms/migrations/0003_alter_form_fields.py +20 -0
  19. wagtailstreamforms/migrations/__init__.py +0 -0
  20. wagtailstreamforms/models/__init__.py +39 -0
  21. wagtailstreamforms/models/abstract.py +15 -0
  22. wagtailstreamforms/models/file.py +35 -0
  23. wagtailstreamforms/models/form.py +188 -0
  24. wagtailstreamforms/models/submission.py +27 -0
  25. wagtailstreamforms/serializers.py +11 -0
  26. wagtailstreamforms/streamfield.py +48 -0
  27. wagtailstreamforms/templates/streamforms/advanced_settings.html +25 -0
  28. wagtailstreamforms/templates/streamforms/confirm_copy.html +26 -0
  29. wagtailstreamforms/templates/streamforms/confirm_delete.html +23 -0
  30. wagtailstreamforms/templates/streamforms/form_block.html +10 -0
  31. wagtailstreamforms/templates/streamforms/index_submissions.html +104 -0
  32. wagtailstreamforms/templates/streamforms/list_submissions.html +56 -0
  33. wagtailstreamforms/templates/streamforms/non_existent_form.html +2 -0
  34. wagtailstreamforms/templates/streamforms/partials/form_field.html +6 -0
  35. wagtailstreamforms/templates/streamforms/partials/pagination_nav.html +17 -0
  36. wagtailstreamforms/templates/streamforms/wagtailadmin/shared/datetimepicker_translations.html +39 -0
  37. wagtailstreamforms/templatetags/__init__.py +0 -0
  38. wagtailstreamforms/templatetags/streamforms_tags.py +43 -0
  39. wagtailstreamforms/urls.py +31 -0
  40. wagtailstreamforms/utils/__init__.py +0 -0
  41. wagtailstreamforms/utils/apps.py +25 -0
  42. wagtailstreamforms/utils/general.py +6 -0
  43. wagtailstreamforms/utils/loading.py +30 -0
  44. wagtailstreamforms/utils/requests.py +13 -0
  45. wagtailstreamforms/utils/version.py +0 -0
  46. wagtailstreamforms/views/__init__.py +4 -0
  47. wagtailstreamforms/views/advanced_settings.py +51 -0
  48. wagtailstreamforms/views/copy.py +83 -0
  49. wagtailstreamforms/views/submission_delete.py +73 -0
  50. wagtailstreamforms/views/submission_list.py +112 -0
  51. wagtailstreamforms/wagtail_hooks.py +255 -0
  52. wagtailstreamforms/wagtailstreamforms_fields.py +274 -0
  53. wagtailstreamforms/wagtailstreamforms_hooks.py +31 -0
  54. wagtailstreamforms-0.0.0.dist-info/METADATA +24 -0
  55. wagtailstreamforms-0.0.0.dist-info/RECORD +58 -0
  56. wagtailstreamforms-0.0.0.dist-info/WHEEL +5 -0
  57. wagtailstreamforms-0.0.0.dist-info/licenses/LICENSE +19 -0
  58. wagtailstreamforms-0.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,255 @@
1
+ from django.contrib import messages
2
+ from django.contrib.admin.utils import quote
3
+ from django.shortcuts import redirect
4
+ from django.template.response import TemplateResponse
5
+ from django.urls import include, path, reverse
6
+ from django.utils.translation import gettext_lazy as _
7
+ from generic_chooser.views import ModelChooserViewSet
8
+ from wagtail import hooks
9
+ from wagtail.admin import messages as wagtail_messages
10
+ from wagtail.admin.widgets import AdminPageChooser
11
+ from wagtail_modeladmin.helpers import AdminURLHelper, ButtonHelper
12
+ from wagtail_modeladmin.options import ModelAdmin, modeladmin_register
13
+ from wagtail_modeladmin.views import (
14
+ CreateView,
15
+ DeleteView,
16
+ EditView,
17
+ InspectView,
18
+ )
19
+
20
+ from wagtailstreamforms import hooks as form_hooks
21
+ from wagtailstreamforms.conf import get_setting
22
+ from wagtailstreamforms.models import Form
23
+ from wagtailstreamforms.utils.loading import get_advanced_settings_model
24
+ from wagtailstreamforms.utils.requests import get_form_instance_from_request
25
+
26
+ SettingsModel = get_advanced_settings_model()
27
+
28
+
29
+ class FormURLHelper(AdminURLHelper):
30
+ def get_action_url(self, action, *args, **kwargs):
31
+ if action in ["advanced", "copy", "submissions"]:
32
+ return reverse("wagtailstreamforms:streamforms_%s" % action, args=args, kwargs=kwargs)
33
+
34
+ return super().get_action_url(action, *args, **kwargs)
35
+
36
+
37
+ class FormButtonHelper(ButtonHelper):
38
+ def button(self, pk, action, label, title, classnames_add, classnames_exclude):
39
+ cn = self.finalise_classname(classnames_add, classnames_exclude)
40
+ button = {
41
+ "url": self.url_helper.get_action_url(action, quote(pk)),
42
+ "label": label,
43
+ "classname": cn,
44
+ "title": title,
45
+ }
46
+
47
+ return button
48
+
49
+ def get_buttons_for_obj(self, obj, exclude=None, classnames_add=None, classnames_exclude=None):
50
+ buttons = super().get_buttons_for_obj(obj, exclude, classnames_add, classnames_exclude)
51
+ pk = getattr(obj, self.opts.pk.attname)
52
+ ph = self.permission_helper
53
+ usr = self.request.user
54
+
55
+ # if there is a form settings model defined
56
+ # users that either create or edit forms should be able edit advanced settings
57
+ if SettingsModel and (ph.user_can_create(usr) or ph.user_can_edit_obj(usr, obj)):
58
+ buttons.append(
59
+ self.button(
60
+ pk,
61
+ "advanced",
62
+ _("Advanced"),
63
+ _("Advanced settings"),
64
+ classnames_add,
65
+ classnames_exclude,
66
+ )
67
+ )
68
+
69
+ # users that can create forms can copy them
70
+ if ph.user_can_create(usr):
71
+ buttons.append(
72
+ self.button(
73
+ pk,
74
+ "copy",
75
+ _("Copy"),
76
+ _("Copy this form"),
77
+ classnames_add,
78
+ classnames_exclude,
79
+ )
80
+ )
81
+
82
+ # users that can do any form actions can view submissions
83
+ buttons.append(
84
+ self.button(
85
+ pk,
86
+ "submissions",
87
+ _("Submissions"),
88
+ _("Submissions of this form"),
89
+ classnames_add,
90
+ classnames_exclude,
91
+ )
92
+ )
93
+
94
+ return buttons
95
+
96
+
97
+ class InstanceSpecificViewHookMixin:
98
+ """Mixin class responsible to apply wagtailstreamforms specific hooks."""
99
+
100
+ def dispatch(self, request, *args, **kwargs):
101
+ for fn in form_hooks.get_hooks("before_inspect_form_instance_dispatch"):
102
+ self.instance = fn(self.instance, request)
103
+ return super().dispatch(request, *args, **kwargs)
104
+
105
+
106
+ class InspectFormView(InstanceSpecificViewHookMixin, InspectView):
107
+ pass
108
+
109
+
110
+ class CreateFormView(CreateView):
111
+ def form_valid(self, form):
112
+ instance = form.save(commit=False)
113
+ for fn in form_hooks.get_hooks("before_form_save"):
114
+ instance = fn(instance, self.request)
115
+ instance.save()
116
+ wagtail_messages.success(
117
+ self.request,
118
+ self.get_success_message(instance),
119
+ buttons=self.get_success_message_buttons(instance),
120
+ )
121
+ return redirect(self.get_success_url())
122
+
123
+
124
+ class EditFormView(InstanceSpecificViewHookMixin, EditView):
125
+ pass
126
+
127
+
128
+ class DeleteFormView(InstanceSpecificViewHookMixin, DeleteView):
129
+ pass
130
+
131
+
132
+ @modeladmin_register
133
+ class FormModelAdmin(ModelAdmin):
134
+ model = Form
135
+ list_display = ("title", "slug", "latest_submission", "saved_submissions")
136
+ list_filter = None
137
+ menu_label = _(get_setting("ADMIN_MENU_LABEL"))
138
+ menu_order = get_setting("ADMIN_MENU_ORDER")
139
+ menu_icon = "form"
140
+ search_fields = ("title", "slug")
141
+ button_helper_class = FormButtonHelper
142
+ inspect_view_class = InspectFormView
143
+ create_view_class = CreateFormView
144
+ edit_view_class = EditFormView
145
+ delete_view_class = DeleteFormView
146
+ url_helper_class = FormURLHelper
147
+ index_template_name = "modeladmin/index.html"
148
+
149
+ def get_queryset(self, request):
150
+ qs = super().get_queryset(request)
151
+ for fn in form_hooks.get_hooks("construct_form_queryset"):
152
+ qs = fn(qs, request)
153
+ return qs
154
+
155
+ def get_list_display(self, request):
156
+ list_display = self.list_display
157
+ for fn in form_hooks.get_hooks("construct_form_list_display"):
158
+ list_display = fn(list_display, request)
159
+ return list_display
160
+
161
+ def get_list_filter(self, request):
162
+ list_filter = self.list_filter
163
+ for fn in form_hooks.get_hooks("construct_form_list_filter"):
164
+ list_filter = fn(list_filter, request)
165
+ return list_filter
166
+
167
+ def latest_submission(self, obj):
168
+ submission_class = obj.get_submission_class()
169
+ return submission_class._default_manager.filter(form=obj).latest("submit_time").submit_time
170
+
171
+ latest_submission.short_description = _("Latest submission")
172
+
173
+ def saved_submissions(self, obj):
174
+ submission_class = obj.get_submission_class()
175
+ return submission_class._default_manager.filter(form=obj).count()
176
+
177
+ saved_submissions.short_description = _("Saved submissions")
178
+
179
+
180
+ @hooks.register("register_admin_urls")
181
+ def register_admin_urls():
182
+ from wagtailstreamforms import urls
183
+
184
+ return [path("wagtailstreamforms/", include((urls, "wagtailstreamforms")))]
185
+
186
+
187
+ @hooks.register("before_serve_page")
188
+ def process_form(page, request, *args, **kwargs):
189
+ """Process the form if there is one, if not just continue."""
190
+
191
+ # only process if settings.WAGTAILSTREAMFORMS_ENABLE_FORM_PROCESSING is True
192
+ if not get_setting("ENABLE_FORM_PROCESSING"):
193
+ return
194
+
195
+ if request.method == "POST":
196
+ form_def = get_form_instance_from_request(request)
197
+
198
+ if form_def:
199
+ form = form_def.get_form(request.POST, request.FILES, page=page, user=request.user)
200
+ context = page.get_context(request, *args, **kwargs)
201
+
202
+ if form.is_valid():
203
+ # process the form submission
204
+ form_def.process_form_submission(form)
205
+
206
+ # create success message
207
+ if form_def.success_message:
208
+ messages.success(request, form_def.success_message, fail_silently=True)
209
+
210
+ # redirect to the page defined in the form
211
+ # or the current page as a fallback - this will avoid refreshing
212
+ # and submitting again
213
+ redirect_page = form_def.post_redirect_page or page
214
+
215
+ return redirect(redirect_page.get_url(request), context=context)
216
+
217
+ else:
218
+ # update the context with the invalid form and serve the page
219
+ context.update(
220
+ {
221
+ "invalid_stream_form_reference": form.data.get("form_reference"),
222
+ "invalid_stream_form": form,
223
+ }
224
+ )
225
+
226
+ # create error message
227
+ if form_def.error_message:
228
+ messages.error(request, form_def.error_message, fail_silently=True)
229
+
230
+ return TemplateResponse(
231
+ request, page.get_template(request, *args, **kwargs), context
232
+ )
233
+
234
+
235
+ class WagtailStreamFormsChooserViewSet(ModelChooserViewSet):
236
+ icon = "form"
237
+ model = Form
238
+ page_title = _("Choose a form")
239
+ per_page = 10
240
+
241
+
242
+ class WagtailStreamFormsChooser(AdminPageChooser):
243
+ choose_one_text = _("Choose a form")
244
+ choose_another_text = _("Choose another form")
245
+ link_to_chosen_text = _("Edit this form")
246
+ model = Form
247
+ choose_modal_url_name = "wagtailstreamforms_chooser:choose"
248
+ icon = "form"
249
+
250
+
251
+ @hooks.register("register_admin_viewset")
252
+ def register_wagtailstreamforms_chooser_viewset():
253
+ return WagtailStreamFormsChooserViewSet(
254
+ "wagtailstreamforms_chooser", url_prefix="wagtailstreamforms-chooser"
255
+ )
@@ -0,0 +1,274 @@
1
+ from typing import List, Tuple
2
+
3
+ from django import forms
4
+ from django.utils.translation import gettext_lazy as _
5
+ from wagtail import blocks
6
+
7
+ from wagtailstreamforms.conf import get_setting
8
+ from wagtailstreamforms.fields import BaseField, register
9
+
10
+
11
+ class SingleLineTextField(BaseField):
12
+ field_class = forms.CharField
13
+ label = _("Text field (single line)")
14
+
15
+
16
+ class MultiLineTextField(BaseField):
17
+ field_class = forms.CharField
18
+ widget = forms.widgets.Textarea
19
+ label = _("Text field (multi line)")
20
+
21
+
22
+ class DateField(BaseField):
23
+ field_class = forms.DateField
24
+ icon = "date"
25
+ label = _("Date field")
26
+
27
+
28
+ class DateTimeField(BaseField):
29
+ field_class = forms.DateTimeField
30
+ icon = "time"
31
+ label = _("Time field")
32
+
33
+
34
+ class EmailField(BaseField):
35
+ field_class = forms.EmailField
36
+ icon = "mail"
37
+ label = _("Email field")
38
+
39
+
40
+ class URLField(BaseField):
41
+ field_class = forms.URLField
42
+ icon = "link"
43
+ label = _("URL field")
44
+
45
+
46
+ class NumberField(BaseField):
47
+ field_class = forms.DecimalField
48
+ label = _("Number field")
49
+
50
+
51
+ class DropdownField(BaseField):
52
+ field_class = forms.ChoiceField
53
+ icon = "arrow-down-big"
54
+ label = _("Dropdown field")
55
+
56
+ def get_options(self, block_value):
57
+ options = super().get_options(block_value)
58
+ choices: List[Tuple[str, str]] = []
59
+ for c in block_value.get("choices"):
60
+ if isinstance(c, dict) and c.get("value"):
61
+ choices.append((c["value"].strip(), c["value"].strip()))
62
+ else:
63
+ choices.append((c.strip(), c.strip()))
64
+
65
+ if block_value.get("empty_label"):
66
+ choices.insert(0, ("", block_value.get("empty_label")))
67
+ options.update({"choices": choices})
68
+ return options
69
+
70
+ def get_form_block(self):
71
+ return blocks.StructBlock(
72
+ [
73
+ ("label", blocks.CharBlock()),
74
+ ("help_text", blocks.CharBlock(required=False)),
75
+ ("required", blocks.BooleanBlock(required=False)),
76
+ ("empty_label", blocks.CharBlock(required=False)),
77
+ ("choices", blocks.ListBlock(blocks.CharBlock(label="Option"))),
78
+ ],
79
+ icon=self.icon,
80
+ label=self.label,
81
+ )
82
+
83
+
84
+ class MultiSelectField(BaseField):
85
+ field_class = forms.MultipleChoiceField
86
+ icon = "list-ul"
87
+ label = _("Multiselect field")
88
+
89
+ def get_options(self, block_value):
90
+ options = super().get_options(block_value)
91
+ choices: List[Tuple[str, str]] = []
92
+ for c in block_value.get("choices"):
93
+ if isinstance(c, dict) and c.get("value"):
94
+ choices.append((c["value"].strip(), c["value"].strip()))
95
+ else:
96
+ choices.append((c.strip(), c.strip()))
97
+ options.update({"choices": choices})
98
+ return options
99
+
100
+ def get_form_block(self):
101
+ return blocks.StructBlock(
102
+ [
103
+ ("label", blocks.CharBlock()),
104
+ ("help_text", blocks.CharBlock(required=False)),
105
+ ("required", blocks.BooleanBlock(required=False)),
106
+ ("choices", blocks.ListBlock(blocks.CharBlock(label="Option"))),
107
+ ],
108
+ icon=self.icon,
109
+ label=self.label,
110
+ )
111
+
112
+
113
+ class RadioField(BaseField):
114
+ field_class = forms.ChoiceField
115
+ widget = forms.widgets.RadioSelect
116
+ icon = "radio-empty"
117
+ label = _("Radio buttons")
118
+
119
+ def get_options(self, block_value):
120
+ options = super().get_options(block_value)
121
+ choices: List[Tuple[str, str]] = []
122
+ for c in block_value.get("choices"):
123
+ if isinstance(c, dict) and c.get("value"):
124
+ choices.append((c["value"].strip(), c["value"].strip()))
125
+ else:
126
+ choices.append((c.strip(), c.strip()))
127
+ options.update({"choices": choices})
128
+ return options
129
+
130
+ def get_form_block(self):
131
+ return blocks.StructBlock(
132
+ [
133
+ ("label", blocks.CharBlock()),
134
+ ("help_text", blocks.CharBlock(required=False)),
135
+ ("required", blocks.BooleanBlock(required=False)),
136
+ ("choices", blocks.ListBlock(blocks.CharBlock(label="Option"))),
137
+ ],
138
+ icon=self.icon,
139
+ label=self.label,
140
+ )
141
+
142
+
143
+ class CheckboxesField(BaseField):
144
+ field_class = forms.MultipleChoiceField
145
+ widget = forms.widgets.CheckboxSelectMultiple
146
+ icon = "tick-inverse"
147
+ label = _("Checkboxes")
148
+
149
+ def get_options(self, block_value):
150
+ options = super().get_options(block_value)
151
+ choices: List[Tuple[str, str]] = []
152
+ for c in block_value.get("choices"):
153
+ if isinstance(c, dict) and c.get("value"):
154
+ choices.append((c["value"].strip(), c["value"].strip()))
155
+ else:
156
+ choices.append((c.strip(), c.strip()))
157
+ options.update({"choices": choices})
158
+ return options
159
+
160
+ def get_form_block(self):
161
+ return blocks.StructBlock(
162
+ [
163
+ ("label", blocks.CharBlock()),
164
+ ("help_text", blocks.CharBlock(required=False)),
165
+ ("required", blocks.BooleanBlock(required=False)),
166
+ ("choices", blocks.ListBlock(blocks.CharBlock(label="Option"))),
167
+ ],
168
+ icon=self.icon,
169
+ label=self.label,
170
+ )
171
+
172
+
173
+ class CheckboxField(BaseField):
174
+ field_class = forms.BooleanField
175
+ icon = "tick-inverse"
176
+ label = _("Checkbox field")
177
+
178
+ def get_form_block(self):
179
+ return blocks.StructBlock(
180
+ [
181
+ ("label", blocks.CharBlock()),
182
+ ("help_text", blocks.CharBlock(required=False)),
183
+ ("required", blocks.BooleanBlock(required=False)),
184
+ ],
185
+ icon=self.icon,
186
+ label=self.label,
187
+ )
188
+
189
+
190
+ class HiddenField(BaseField):
191
+ field_class = forms.CharField
192
+ widget = forms.widgets.HiddenInput
193
+ icon = "no-view"
194
+ label = _("Hidden field")
195
+
196
+
197
+ class SingleFileField(BaseField):
198
+ field_class = forms.FileField
199
+ widget = forms.widgets.FileInput
200
+ icon = "doc-full-inverse"
201
+ label = _("File field")
202
+
203
+ def get_form_block(self):
204
+ return blocks.StructBlock(
205
+ [
206
+ ("label", blocks.CharBlock()),
207
+ ("help_text", blocks.CharBlock(required=False)),
208
+ ("required", blocks.BooleanBlock(required=False)),
209
+ ],
210
+ icon=self.icon,
211
+ label=self.label,
212
+ )
213
+
214
+
215
+ class MultipleFileInput(forms.ClearableFileInput):
216
+ allow_multiple_selected = True
217
+
218
+
219
+ class MultipleFileField(forms.FileField):
220
+ def __init__(self, *args, **kwargs):
221
+ kwargs.setdefault("widget", MultipleFileInput())
222
+ super().__init__(*args, **kwargs)
223
+
224
+ def clean(self, data, initial=None):
225
+ single_file_clean = super().clean
226
+ if data and isinstance(data, (list, tuple)):
227
+ result = [single_file_clean(d, initial) for d in data]
228
+ else:
229
+ result = single_file_clean(data, initial)
230
+ return result
231
+
232
+
233
+ class MultiFileField(BaseField):
234
+ field_class = MultipleFileField
235
+ widget = MultipleFileInput(attrs={"multiple": True})
236
+ icon = "doc-full-inverse"
237
+ label = _("Files field")
238
+
239
+ def get_form_block(self):
240
+ return blocks.StructBlock(
241
+ [
242
+ ("label", blocks.CharBlock()),
243
+ ("help_text", blocks.CharBlock(required=False)),
244
+ ("required", blocks.BooleanBlock(required=False)),
245
+ ],
246
+ icon=self.icon,
247
+ label=self.label,
248
+ )
249
+
250
+
251
+ FIELD_MAPPING = {
252
+ "singleline": SingleLineTextField,
253
+ "multiline": MultiLineTextField,
254
+ "date": DateField,
255
+ "datetime": DateTimeField,
256
+ "email": EmailField,
257
+ "url": URLField,
258
+ "number": NumberField,
259
+ "dropdown": DropdownField,
260
+ "radio": RadioField,
261
+ "checkboxes": CheckboxesField,
262
+ "checkbox": CheckboxField,
263
+ "hidden": HiddenField,
264
+ "singlefile": SingleFileField,
265
+ "multifile": MultiFileField,
266
+ }
267
+
268
+ enabled_fields = get_setting("ENABLED_FIELDS")
269
+
270
+ for field_name in enabled_fields:
271
+ cls = FIELD_MAPPING.get(field_name, None)
272
+ if not cls:
273
+ raise KeyError("Field with name '%s' does not exist" % field_name)
274
+ register(field_name, cls)
@@ -0,0 +1,31 @@
1
+ import json
2
+
3
+ from django.template.defaultfilters import pluralize
4
+
5
+ from wagtailstreamforms.hooks import register
6
+ from wagtailstreamforms.models import FormSubmissionFile
7
+ from wagtailstreamforms.serializers import FormSubmissionSerializer
8
+
9
+
10
+ @register("process_form_submission")
11
+ def save_form_submission_data(instance, form):
12
+ """saves the form submission data"""
13
+
14
+ # copy the cleaned_data so we dont mess with the original
15
+ submission_data = form.cleaned_data.copy()
16
+
17
+ # change the submission data to a count of the files
18
+ for field in form.files.keys():
19
+ count = len(form.files.getlist(field))
20
+ submission_data[field] = "{} file{}".format(count, pluralize(count))
21
+
22
+ # save the submission data
23
+ submission = instance.get_submission_class().objects.create(
24
+ form_data=json.dumps(submission_data, cls=FormSubmissionSerializer),
25
+ form=instance,
26
+ )
27
+
28
+ # save the form files
29
+ for field in form.files:
30
+ for file in form.files.getlist(field):
31
+ FormSubmissionFile.objects.create(submission=submission, field=field, file=file)
@@ -0,0 +1,24 @@
1
+ Metadata-Version: 2.4
2
+ Name: wagtailstreamforms
3
+ Version: 0.0.0
4
+ Author-email: Lab Digital BV <info@labdigital.nl>
5
+ License: Proprietary
6
+ Classifier: License :: Other/Proprietary License
7
+ Classifier: Programming Language :: Python
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.9
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Requires-Python: >=3.9
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: wagtail<7.0,>=4.1
18
+ Requires-Dist: Unidecode<2.0,>=0.04.14
19
+ Requires-Dist: wagtail-generic-chooser<0.7,>=0.5.0
20
+ Requires-Dist: wagtail-modeladmin>=2.2.0
21
+ Requires-Dist: django-recaptcha>=4.0.0
22
+ Requires-Dist: psycopg>=3.2.9
23
+ Requires-Dist: tox>=4.26.0
24
+ Dynamic: license-file
@@ -0,0 +1,58 @@
1
+ wagtailstreamforms/__init__.py,sha256=jegJMYuSW90VKiMotVdNAakBvYVPirm-A5oeyJVuhH8,22
2
+ wagtailstreamforms/blocks.py,sha256=IgLGZt8vribjHR_2_Wz7PqmxCs_J5XB5xtMPS6ufI0c,3052
3
+ wagtailstreamforms/conf.py,sha256=-9RxLbU2WifnOItQWsmlCoi1DfbRpPdZMau-cS1oQ20,876
4
+ wagtailstreamforms/fields.py,sha256=Bnsf98OAiKlDw8LVx9u9NNeZ9TG13TigaXU87zjhMF4,6703
5
+ wagtailstreamforms/forms.py,sha256=wwi81uY1Rij-95SsU_NymWuusMUGwsNsOsYg_KCAMuk,1906
6
+ wagtailstreamforms/hooks.py,sha256=mxrYkCooKf5liNFBVraOpTpIzP2hNPdLxdzOQyvF-Ug,1609
7
+ wagtailstreamforms/serializers.py,sha256=GVWe8A1QpKojkMZF8swGQeUcjR-lSf60_zJ9l5mRNmg,305
8
+ wagtailstreamforms/streamfield.py,sha256=al6tM2635E0EUMMylL2gTGB7sIM99tgMhg3PMVPdI2A,1766
9
+ wagtailstreamforms/urls.py,sha256=3cS2XhBjfvrl8iBkGHQF1Db8kmQABdJ3QnR6j4y4f5E,790
10
+ wagtailstreamforms/wagtail_hooks.py,sha256=9xkCvwQHEIAfVy1gv47ZVhk164IMoXWY8eLiQJOLoUM,8910
11
+ wagtailstreamforms/wagtailstreamforms_fields.py,sha256=iHyiy75mAkxu-6uBqFzj7BOTLzmnZwGCqyC22y58a7w,8264
12
+ wagtailstreamforms/wagtailstreamforms_hooks.py,sha256=06FyWnNspNhDkCzV8Sikjm-cb9UrLm5N43lQ-uN-PIQ,1092
13
+ wagtailstreamforms/locale/es/LC_MESSAGES/django.mo,sha256=5QbzAOL5FsDtu2uT0j4hHh3gSm4tMfFydLvhQXp66KE,5217
14
+ wagtailstreamforms/locale/es/LC_MESSAGES/django.po,sha256=GAfbXi4qmeN76M_g_cWV5H76fl9oG7mYLhXDjEko1G4,7797
15
+ wagtailstreamforms/locale/nl/LC_MESSAGES/django.mo,sha256=Lq6G1E8z784nbXJZzeCX_a28rnbmC2k0PwactxP5Kcw,5391
16
+ wagtailstreamforms/locale/nl/LC_MESSAGES/django.po,sha256=N-R8N6TLvtelylyW4u9lGfHDN4_xglZupAuqiBBEli4,11227
17
+ wagtailstreamforms/locale/tr/LC_MESSAGES/django.mo,sha256=Pz4EEZkaA2w2zpHd9W-9w-CKO8S7J4UXe_qA6HLqeIw,379
18
+ wagtailstreamforms/locale/tr/LC_MESSAGES/django.po,sha256=CB2ilvnbsh6fJTOxm_Cvs8VhQASNGDhtkP3nKBc94hg,5948
19
+ wagtailstreamforms/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
+ wagtailstreamforms/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ wagtailstreamforms/management/commands/prunesubmissions.py,sha256=3T9Oa1x2peYj4RRsSB788vnsl-bNG_RBkfUIz0zB7mA,877
22
+ wagtailstreamforms/migrations/0001_initial.py,sha256=um6loNvjplnsKM4geqFlXWBfXqb-gUNylxQBqNU13Vs,25291
23
+ wagtailstreamforms/migrations/0002_form_site.py,sha256=Cb8ylWAKcAG0GlvSf3rg2jczJd-bPn5IKPMYBhwb_JA,553
24
+ wagtailstreamforms/migrations/0003_alter_form_fields.py,sha256=lbm6BXwpxZ2jXe_qGvKC1t4K_xbnldws13MMO7Rtppc,4656
25
+ wagtailstreamforms/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
+ wagtailstreamforms/models/__init__.py,sha256=GoTADDaGEIKmuLeBOLDi9899xlhiXvQCC9GQdAUK8AQ,1393
27
+ wagtailstreamforms/models/abstract.py,sha256=iPTVroEQI56g7GLBmu_s12UQPVFx_Ls2Bb3Vq8Psco4,318
28
+ wagtailstreamforms/models/file.py,sha256=TCzJoH3qW1neoCl-iBFyDGg8vbrHzeksg-Cp67fva1I,1002
29
+ wagtailstreamforms/models/form.py,sha256=qmGPNbfEaSNF__OhVVec-U08OVmD7wjJ7sVZ1X3FVGA,6015
30
+ wagtailstreamforms/models/submission.py,sha256=SjmgocMfCyEJZWnkjTm7XXftKBNNMUpRbTclZX9CaYE,739
31
+ wagtailstreamforms/templates/streamforms/advanced_settings.html,sha256=wsEqMxG0rBrQO3vN0r4eGa4nlEivTbwXNMqluly2anw,999
32
+ wagtailstreamforms/templates/streamforms/confirm_copy.html,sha256=Tlq27K7FH3ZwBdmJE_e7lwiUFtkv1045apTm2RbOqbM,1051
33
+ wagtailstreamforms/templates/streamforms/confirm_delete.html,sha256=99Z3ujkiJQh46Dejg8Vqd7NciJgoSL9wzhZNX4aUR50,1040
34
+ wagtailstreamforms/templates/streamforms/form_block.html,sha256=G0-vYbEjvDCrgMEJw8zLrB60dtYeKjjleDlsq-Qfncw,471
35
+ wagtailstreamforms/templates/streamforms/index_submissions.html,sha256=h8VLgyY-6dedXurrx7XPRAZxcoM99Z_q3QspKMB6ydU,4996
36
+ wagtailstreamforms/templates/streamforms/list_submissions.html,sha256=kYtmQYEWMvjFW_KT2ftxE__i_V3jlcYIV7a-x7dd284,1801
37
+ wagtailstreamforms/templates/streamforms/non_existent_form.html,sha256=tolwhcWcupsok-bKaTqGd3Tv0LRbg2j4XIt0SL401Lw,71
38
+ wagtailstreamforms/templates/streamforms/partials/form_field.html,sha256=vRhNs8mHercJOTq5T8pJh0Eq6I-YkOCciPKL4PWVmMs,181
39
+ wagtailstreamforms/templates/streamforms/partials/pagination_nav.html,sha256=wK7bY4vSP3a9sP9RFr--urH1cUNdITmWJRwHsl_tH0w,692
40
+ wagtailstreamforms/templates/streamforms/wagtailadmin/shared/datetimepicker_translations.html,sha256=1sC0EsFoVAhGZo34CS1OIgzc25BDX5rvlC6ON6yPuCQ,1183
41
+ wagtailstreamforms/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
+ wagtailstreamforms/templatetags/streamforms_tags.py,sha256=hBnqUvnZ1AwM2FsuCuZM02nriUZPP7g2d2jm_TkeFW4,1219
43
+ wagtailstreamforms/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
+ wagtailstreamforms/utils/apps.py,sha256=4AIcASy9nIgHDDnyHxKvWZkx8E-bDwBK50YwJtO7O84,685
45
+ wagtailstreamforms/utils/general.py,sha256=LijQjiWQiF4uzgRfnny1hoFjg1TMgiUgjOH1TPvCkWw,152
46
+ wagtailstreamforms/utils/loading.py,sha256=wSAdrXDcXGyXSnNWD1fC2Ir1fD4qnzKQHkSgGD0pu2A,990
47
+ wagtailstreamforms/utils/requests.py,sha256=xSE4wo8-z_ZhpVm53RqAcUllVgSyIgw3MqYROf6Cixk,351
48
+ wagtailstreamforms/utils/version.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
+ wagtailstreamforms/views/__init__.py,sha256=nwvG8U51a0D-j4gnpuoM1KTCAljN0ijSl43E6PlrqbE,215
50
+ wagtailstreamforms/views/advanced_settings.py,sha256=6o8cDw-RIejnWFwTtG44vKarJlz03Pb7GtxCM8BUuns,1527
51
+ wagtailstreamforms/views/copy.py,sha256=m-pt86NoTT8AKTiNrzmpd82eZEqDK65LGUIBGFWx--U,2698
52
+ wagtailstreamforms/views/submission_delete.py,sha256=FXvFmgoguliZxL3vf44BIYF73ay9mFnpUT-ovehPoWs,2578
53
+ wagtailstreamforms/views/submission_list.py,sha256=K18UtXmc4uwA3gxfWtgJifNvVnXCtW8nnvVzCNuiUJM,4019
54
+ wagtailstreamforms-0.0.0.dist-info/licenses/LICENSE,sha256=Oy8Ub6BG7n1hon-euIrnTvJ1wvgm9ZANjslBA3FY4hc,1066
55
+ wagtailstreamforms-0.0.0.dist-info/METADATA,sha256=3I1LUIz4ZeC2Lhp6vhTAnTaCAdH74lQAqq5DiHVwiuI,894
56
+ wagtailstreamforms-0.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
57
+ wagtailstreamforms-0.0.0.dist-info/top_level.txt,sha256=rdaWLHEw0fFv5h1bdErzwQW8Bd1JSDnR8s322dj6iE8,19
58
+ wagtailstreamforms-0.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,19 @@
1
+ Copyright (C) 2018 Accent Design Group LTD
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ wagtailstreamforms