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,43 @@
1
+ from urllib.parse import urlencode
2
+
3
+ from django.template import Library
4
+ from django.utils.safestring import mark_safe
5
+
6
+ from wagtailstreamforms.blocks import WagtailFormBlock
7
+ from wagtailstreamforms.models import Form
8
+
9
+ register = Library()
10
+
11
+
12
+ @register.simple_tag(takes_context=True)
13
+ def url_replace(context, **kwargs):
14
+ """will append kwargs to the existing url replacing any passed in"""
15
+
16
+ query = context["request"].GET.dict()
17
+ query.update(kwargs)
18
+ return urlencode(query)
19
+
20
+
21
+ @register.simple_tag(takes_context=True)
22
+ def streamforms_form(context, slug, reference, action=".", **kwargs):
23
+ """
24
+ Renders a form on the page.
25
+
26
+ {% load streamforms_tags %}
27
+ {% streamforms_form "the-form-slug" "some-unique-reference" "." %}
28
+ """
29
+
30
+ try:
31
+ form = Form.objects.get(slug=slug)
32
+
33
+ block = WagtailFormBlock()
34
+
35
+ # the context is a RequestContext, we need to turn it into a dict or
36
+ # the blocks in wagtail will start to fail with dict(context)
37
+ return block.render(
38
+ block.to_python({"form": form.pk, "form_action": action, "form_reference": reference}),
39
+ context.flatten(),
40
+ )
41
+
42
+ except Form.DoesNotExist:
43
+ return mark_safe("")
@@ -0,0 +1,31 @@
1
+ from django.urls import path
2
+
3
+ from wagtailstreamforms import views
4
+ from wagtailstreamforms.utils.loading import get_advanced_settings_model
5
+
6
+ SettingsModel = get_advanced_settings_model()
7
+
8
+
9
+ urlpatterns = [
10
+ path("<int:pk>/copy/", views.CopyFormView.as_view(), name="streamforms_copy"),
11
+ path(
12
+ "<int:pk>/submissions/",
13
+ views.SubmissionListView.as_view(),
14
+ name="streamforms_submissions",
15
+ ),
16
+ path(
17
+ "<int:pk>/submissions/delete/",
18
+ views.SubmissionDeleteView.as_view(),
19
+ name="streamforms_delete_submissions",
20
+ ),
21
+ ]
22
+
23
+
24
+ if SettingsModel: # pragma: no cover
25
+ urlpatterns += [
26
+ path(
27
+ "<int:pk>/advanced/",
28
+ views.AdvancedSettingsView.as_view(),
29
+ name="streamforms_advanced",
30
+ )
31
+ ]
File without changes
@@ -0,0 +1,25 @@
1
+ from importlib import import_module
2
+
3
+ from django.apps import apps
4
+ from django.utils.module_loading import module_has_submodule
5
+
6
+
7
+ def get_app_modules():
8
+ """
9
+ Generator function that yields a module object for each installed app
10
+ yields tuples of (app_name, module)
11
+ """
12
+
13
+ for app in apps.get_app_configs():
14
+ yield app.name, app.module
15
+
16
+
17
+ def get_app_submodules(submodule_name):
18
+ """
19
+ Searches each app module for the specified submodule
20
+ yields tuples of (app_name, module)
21
+ """
22
+
23
+ for name, module in get_app_modules():
24
+ if module_has_submodule(module, submodule_name):
25
+ yield name, import_module("%s.%s" % (name, submodule_name))
@@ -0,0 +1,6 @@
1
+ from django.utils.text import slugify
2
+ from unidecode import unidecode
3
+
4
+
5
+ def get_slug_from_string(label):
6
+ return str(slugify(str(unidecode(label))))
@@ -0,0 +1,30 @@
1
+ from django.apps import apps
2
+ from django.core.exceptions import ImproperlyConfigured
3
+
4
+ from wagtailstreamforms.conf import get_setting
5
+ from wagtailstreamforms.models import AbstractFormSetting
6
+
7
+
8
+ def get_advanced_settings_model():
9
+ """
10
+ Returns the advanced form settings model if one is defined
11
+ """
12
+
13
+ model = get_setting("ADVANCED_SETTINGS_MODEL")
14
+
15
+ if not model:
16
+ return
17
+
18
+ def raise_error(msg):
19
+ setting = "WAGTAILSTREAMFORMS_ADVANCED_SETTINGS_MODEL"
20
+ raise ImproperlyConfigured("%s %s" % (setting, msg))
21
+
22
+ try:
23
+ model_class = apps.get_model(model, require_ready=False)
24
+ if issubclass(model_class, AbstractFormSetting):
25
+ return model_class
26
+ raise_error("must inherit from 'wagtailstreamforms.models.AbstractFormSetting'")
27
+ except ValueError:
28
+ raise_error("must be of the form 'app_label.model_name'")
29
+ except LookupError:
30
+ raise_error("refers to model '%s' that has not been installed" % model)
@@ -0,0 +1,13 @@
1
+ from wagtailstreamforms.models import Form
2
+
3
+
4
+ def get_form_instance_from_request(request):
5
+ """Get the form class from the request."""
6
+
7
+ form_id = request.POST.get("form_id")
8
+ if form_id and form_id.isdigit():
9
+ try:
10
+ return Form.objects.get(pk=int(form_id))
11
+ except Form.DoesNotExist:
12
+ pass
13
+ return None
File without changes
@@ -0,0 +1,4 @@
1
+ from .advanced_settings import AdvancedSettingsView # noqa
2
+ from .copy import CopyFormView # noqa
3
+ from .submission_delete import SubmissionDeleteView # noqa
4
+ from .submission_list import SubmissionListView # noqa
@@ -0,0 +1,51 @@
1
+ from django import forms
2
+ from django.contrib import messages
3
+ from django.shortcuts import get_object_or_404
4
+ from django.utils.translation import gettext as _
5
+ from django.views.generic import UpdateView
6
+
7
+ from wagtailstreamforms.models import Form
8
+ from wagtailstreamforms.utils.loading import get_advanced_settings_model
9
+ from wagtailstreamforms.wagtail_hooks import FormURLHelper
10
+
11
+ SettingsModel = get_advanced_settings_model()
12
+
13
+
14
+ class AdvancedSettingsForm(forms.ModelForm):
15
+ class Meta:
16
+ exclude = ("form",)
17
+ model = SettingsModel
18
+
19
+
20
+ class AdvancedSettingsView(UpdateView):
21
+ form_class = AdvancedSettingsForm
22
+ model = SettingsModel
23
+ template_name = "streamforms/advanced_settings.html"
24
+ success_message = _("Form '%s' advanced settings updated.")
25
+
26
+ @property
27
+ def url_helper(self):
28
+ return FormURLHelper(model=Form)
29
+
30
+ def get_object(self, queryset=None):
31
+ form_pk = self.kwargs.get("pk")
32
+ form = get_object_or_404(Form, pk=form_pk)
33
+
34
+ try:
35
+ obj = self.model.objects.get(form=form)
36
+ except self.model.DoesNotExist:
37
+ obj = self.model(form=form)
38
+
39
+ return obj
40
+
41
+ def form_valid(self, form):
42
+ response = super().form_valid(form)
43
+ self.create_success_message()
44
+ return response
45
+
46
+ def create_success_message(self):
47
+ success_message = self.success_message % self.object.form
48
+ messages.success(self.request, success_message)
49
+
50
+ def get_success_url(self):
51
+ return self.url_helper.index_url
@@ -0,0 +1,83 @@
1
+ from django import forms
2
+ from django.contrib import messages
3
+ from django.core.exceptions import PermissionDenied
4
+ from django.http import HttpResponseRedirect
5
+ from django.utils.translation import gettext_lazy as _
6
+ from django.views.generic.detail import (
7
+ BaseDetailView,
8
+ SingleObjectTemplateResponseMixin,
9
+ )
10
+ from wagtail_modeladmin.helpers import PermissionHelper
11
+
12
+ from wagtailstreamforms.models import Form
13
+ from wagtailstreamforms.wagtail_hooks import FormURLHelper
14
+
15
+
16
+ class CopyForm(forms.Form):
17
+ title = forms.CharField(label=_("New title"))
18
+ slug = forms.SlugField(label=_("New slug"))
19
+
20
+ def clean_slug(self):
21
+ slug = self.cleaned_data["slug"]
22
+ if Form.objects.filter(slug=slug).exists():
23
+ raise forms.ValidationError("This slug is already in use")
24
+ return slug
25
+
26
+
27
+ class CopyFormView(SingleObjectTemplateResponseMixin, BaseDetailView):
28
+ model = Form
29
+ template_name = "streamforms/confirm_copy.html"
30
+ success_message = _("Form '%s' copied to '%s'.")
31
+
32
+ @property
33
+ def permission_helper(self):
34
+ return PermissionHelper(model=self.model)
35
+
36
+ @property
37
+ def url_helper(self):
38
+ return FormURLHelper(model=self.model)
39
+
40
+ def dispatch(self, request, *args, **kwargs):
41
+ self.object = self.get_object()
42
+ if not self.permission_helper.user_can_create(self.request.user):
43
+ raise PermissionDenied
44
+ return super().dispatch(request, *args, **kwargs)
45
+
46
+ def get_object(self, queryset=None):
47
+ obj = super().get_object(queryset)
48
+ return obj
49
+
50
+ def copy(self, request, *args, **kwargs):
51
+ form = CopyForm(request.POST)
52
+
53
+ if form.is_valid():
54
+ copied = self.object.copy()
55
+ copied.title = form.cleaned_data["title"]
56
+ copied.slug = form.cleaned_data["slug"]
57
+
58
+ copied.save()
59
+
60
+ self.create_success_message(copied)
61
+
62
+ return HttpResponseRedirect(self.get_success_url())
63
+
64
+ context = self.get_context_data(object=self.object)
65
+ context["form"] = form
66
+
67
+ return self.render_to_response(context)
68
+
69
+ def get(self, request, *args, **kwargs):
70
+ context = self.get_context_data(object=self.object)
71
+ context["form"] = CopyForm(initial={"title": self.object.title, "slug": self.object.slug})
72
+
73
+ return self.render_to_response(context)
74
+
75
+ def post(self, request, *args, **kwargs):
76
+ return self.copy(request, *args, **kwargs)
77
+
78
+ def create_success_message(self, copied):
79
+ success_message = self.success_message % (self.object, copied)
80
+ messages.success(self.request, success_message)
81
+
82
+ def get_success_url(self):
83
+ return self.url_helper.index_url
@@ -0,0 +1,73 @@
1
+ from django.contrib import messages
2
+ from django.core.exceptions import PermissionDenied
3
+ from django.http import HttpResponseRedirect
4
+ from django.urls import reverse
5
+ from django.utils.translation import ngettext
6
+ from django.views.generic import DeleteView
7
+ from wagtail_modeladmin.helpers import PermissionHelper
8
+
9
+ from wagtailstreamforms.models import Form
10
+
11
+
12
+ class SubmissionDeleteView(DeleteView):
13
+ model = Form
14
+ template_name = "streamforms/confirm_delete.html"
15
+
16
+ @property
17
+ def permission_helper(self):
18
+ return PermissionHelper(model=self.model)
19
+
20
+ def dispatch(self, request, *args, **kwargs):
21
+ self.object = self.get_object()
22
+ if not self.permission_helper.user_can_delete_obj(self.request.user, self.object):
23
+ raise PermissionDenied
24
+ return super().dispatch(request, *args, **kwargs)
25
+
26
+ def get_object(self, queryset=None):
27
+ obj = super().get_object(queryset)
28
+ return obj
29
+
30
+ def get_submissions(self):
31
+ submission_ids = self.request.GET.getlist("selected-submissions")
32
+ submission_class = self.object.get_submission_class()
33
+ return submission_class._default_manager.filter(id__in=submission_ids)
34
+
35
+ def get_context_data(self, **kwargs):
36
+ context = super().get_context_data(**kwargs)
37
+ context["submissions"] = self.get_submissions()
38
+ return context
39
+
40
+ def delete(self, request, *args, **kwargs):
41
+ """
42
+ Django 4.0 uses FormMixin, so this logic has been moved to form_valid.
43
+
44
+ This can be removed once Django 3.2 is no longer supported.
45
+ """
46
+ success_url = self.get_success_url()
47
+ submissions = self.get_submissions()
48
+ count = submissions.count()
49
+ submissions.delete()
50
+ self.create_success_message(count)
51
+ return HttpResponseRedirect(success_url)
52
+
53
+ def form_valid(self, request, *args, **kwargs):
54
+ success_url = self.get_success_url()
55
+ submissions = self.get_submissions()
56
+ count = submissions.count()
57
+ submissions.delete()
58
+ self.create_success_message(count)
59
+ return HttpResponseRedirect(success_url)
60
+
61
+ def create_success_message(self, count):
62
+ messages.success(
63
+ self.request,
64
+ ngettext(
65
+ "One submission has been deleted.",
66
+ "%(count)d submissions have been deleted.",
67
+ count,
68
+ )
69
+ % {"count": count},
70
+ )
71
+
72
+ def get_success_url(self):
73
+ return reverse("wagtailstreamforms:streamforms_submissions", kwargs={"pk": self.object.pk})
@@ -0,0 +1,112 @@
1
+ import csv
2
+ import datetime
3
+
4
+ from django.core.exceptions import PermissionDenied
5
+ from django.http import Http404, HttpResponse
6
+ from django.utils.encoding import smart_str
7
+ from django.utils.translation import gettext as _
8
+ from django.views.generic import ListView
9
+ from django.views.generic.detail import SingleObjectMixin
10
+ from wagtail_modeladmin.helpers import PermissionHelper
11
+
12
+ from wagtailstreamforms import hooks
13
+ from wagtailstreamforms.forms import SelectDateForm
14
+ from wagtailstreamforms.models import Form
15
+
16
+
17
+ class SubmissionListView(SingleObjectMixin, ListView):
18
+ paginate_by = 25
19
+ page_kwarg = "p"
20
+ template_name = "streamforms/index_submissions.html"
21
+ filter_form = None
22
+ model = Form
23
+
24
+ @property
25
+ def permission_helper(self):
26
+ return PermissionHelper(model=self.model)
27
+
28
+ def dispatch(self, request, *args, **kwargs):
29
+ self.object = self.get_object()
30
+ if not self.permission_helper.user_can_list(self.request.user):
31
+ raise PermissionDenied
32
+ return super().dispatch(request, *args, **kwargs)
33
+
34
+ def get_object(self, queryset=None):
35
+ pk = self.kwargs.get(self.pk_url_kwarg)
36
+ try:
37
+ qs = self.model.objects.all()
38
+ for fn in hooks.get_hooks("construct_form_queryset"):
39
+ qs = fn(qs, self.request)
40
+ return qs.get(pk=pk)
41
+ except self.model.DoesNotExist:
42
+ raise Http404(_("No Form found matching the query"))
43
+
44
+ def get(self, request, *args, **kwargs):
45
+ self.filter_form = SelectDateForm(request.GET)
46
+
47
+ if request.GET.get("action") == "CSV":
48
+ return self.csv()
49
+
50
+ return super().get(request, *args, **kwargs)
51
+
52
+ def csv(self):
53
+ queryset = self.get_queryset()
54
+ data_fields = self.object.get_data_fields()
55
+ data_headings = [smart_str(label) for name, label in data_fields]
56
+
57
+ response = HttpResponse(content_type="text/csv; charset=utf-8")
58
+ response["Content-Disposition"] = "attachment;filename=export.csv"
59
+
60
+ writer = csv.writer(response)
61
+ writer.writerow(data_headings)
62
+ for s in queryset:
63
+ data_row = []
64
+ form_data = s.get_data()
65
+ for name, label in data_fields:
66
+ data_row.append(smart_str(form_data.get(name)))
67
+ writer.writerow(data_row)
68
+
69
+ return response
70
+
71
+ def get_queryset(self):
72
+ submission_class = self.object.get_submission_class()
73
+ self.queryset = submission_class._default_manager.filter(form=self.object)
74
+
75
+ # filter the queryset by the required dates
76
+ if self.filter_form.is_valid():
77
+ date_from = self.filter_form.cleaned_data.get("date_from")
78
+ date_to = self.filter_form.cleaned_data.get("date_to")
79
+ if date_from:
80
+ self.queryset = self.queryset.filter(submit_time__gte=date_from)
81
+ if date_to:
82
+ date_to += datetime.timedelta(days=1)
83
+ self.queryset = self.queryset.filter(submit_time__lte=date_to)
84
+
85
+ return self.queryset.prefetch_related("files")
86
+
87
+ def get_context_data(self, **kwargs):
88
+ context = super().get_context_data(**kwargs)
89
+
90
+ data_fields = self.object.get_data_fields()
91
+ data_headings = [label for name, label in data_fields]
92
+
93
+ # populate data rows from paginator
94
+ data_rows = []
95
+ for s in context["page_obj"]:
96
+ form_data = s.get_data()
97
+ form_files = s.files.all()
98
+ data_row = [form_data.get(name) for name, label in data_fields]
99
+ data_rows.append({"model_id": s.id, "fields": data_row, "files": form_files})
100
+
101
+ context.update(
102
+ {
103
+ "filter_form": self.filter_form,
104
+ "data_rows": data_rows,
105
+ "data_headings": data_headings,
106
+ "has_delete_permission": self.permission_helper.user_can_delete_obj(
107
+ self.request.user, self.object
108
+ ),
109
+ }
110
+ )
111
+
112
+ return context