From 46b4856a0c880109ff73cc7ed34beb01a6284952 Mon Sep 17 00:00:00 2001 From: Ryan Sullivan Date: Fri, 11 Sep 2020 23:50:08 -0400 Subject: [PATCH 01/33] Some new extension points --- wagtailstreamforms/models/form.py | 136 ++++++++++++++++-------------- wagtailstreamforms/streamfield.py | 12 ++- 2 files changed, 84 insertions(+), 64 deletions(-) diff --git a/wagtailstreamforms/models/form.py b/wagtailstreamforms/models/form.py index 2c7e8d59..3ab8b37a 100644 --- a/wagtailstreamforms/models/form.py +++ b/wagtailstreamforms/models/form.py @@ -28,20 +28,7 @@ def for_site(self, site): return self.filter(site=site) -class AbstractForm(models.Model): - site = models.ForeignKey(Site, on_delete=models.SET_NULL, null=True, blank=True) - title = models.CharField(_("Title"), max_length=255) - slug = models.SlugField( - _("Slug"), - allow_unicode=True, - max_length=255, - unique=True, - help_text=_("Used to identify the form in template tags"), - ) - template_name = models.CharField( - _("Template"), max_length=255, choices=get_setting("FORM_TEMPLATES") - ) - fields = FormFieldsStreamField([], verbose_name=_("Fields")) +class AbstractFormOuter(models.Model): submit_button_text = models.CharField( _("Submit button text"), max_length=100, default="Submit" ) @@ -50,7 +37,8 @@ class AbstractForm(models.Model): blank=True, max_length=255, help_text=_( - "An optional success message to show when the form has been successfully submitted" + "An optional success message to show when the form has been successfully " + "submitted" ), ) error_message = models.CharField( @@ -74,12 +62,7 @@ class AbstractForm(models.Model): verbose_name=_("Submission hooks"), blank=True ) - objects = FormQuerySet.as_manager() - settings_panels = [ - FieldPanel("title", classname="full"), - FieldPanel("slug"), - FieldPanel("template_name"), FieldPanel("submit_button_text"), MultiFieldPanel( [FieldPanel("success_message"), FieldPanel("error_message")], _("Messages") @@ -88,6 +71,77 @@ class AbstractForm(models.Model): PageChooserPanel("post_redirect_page"), ] + def get_data_fields(self): + """ Returns a list of tuples with (field_name, field_label). """ + + data_fields = [("submit_time", _("Submission date"))] + data_fields += [ + (get_slug_from_string(field["value"]["label"]), field["value"]["label"]) + for field in self.get_form_fields() + ] + + return data_fields + + def get_form(self, *args, **kwargs): + """ Returns the form. """ + + form_class = self.get_form_class() + form_instance = form_class(*args, **kwargs) + return form_instance + + def get_form_class(self): + """ Returns the form class. """ + + return FormBuilder(self.get_form_fields()).get_form_class() + + def get_form_fields(self): + """ Returns the form fields stream_data. """ + + form_fields = self.fields.stream_data + for fn in hooks.get_hooks("construct_submission_form_fields"): + form_fields = fn(form_fields) + return form_fields + + def get_submission_class(self): + """ Returns submission class. """ + + return FormSubmission + + def process_form_submission(self, form): + """ Runs each hook if selected in the form. """ + + for fn in hooks.get_hooks("process_form_submission"): + if fn.__name__ in self.process_form_submission_hooks: + fn(self, form) + + class Meta: + abstract = True + + +class AbstractForm(AbstractFormOuter): + site = models.ForeignKey(Site, on_delete=models.SET_NULL, null=True, blank=True) + title = models.CharField(_("Title"), max_length=255) + slug = models.SlugField( + _("Slug"), + allow_unicode=True, + max_length=255, + unique=True, + help_text=_("Used to identify the form in template tags"), + ) + template_name = models.CharField( + _("Template"), max_length=255, choices=get_setting("FORM_TEMPLATES") + ) + fields = FormFieldsStreamField([], verbose_name=_("Fields")) + + objects = FormQuerySet.as_manager() + + settings_panels = [ + FieldPanel("title", classname="full"), + FieldPanel("slug"), + FieldPanel("template_name"), + *AbstractFormOuter.settings_panels, + ] + field_panels = [StreamFieldPanel("fields")] edit_handler = TabbedInterface( @@ -139,48 +193,6 @@ def copy(self): copy.alters_data = True - def get_data_fields(self): - """ Returns a list of tuples with (field_name, field_label). """ - - data_fields = [("submit_time", _("Submission date"))] - data_fields += [ - (get_slug_from_string(field["value"]["label"]), field["value"]["label"]) - for field in self.get_form_fields() - ] - - return data_fields - - def get_form(self, *args, **kwargs): - """ Returns the form. """ - - form_class = self.get_form_class() - return form_class(*args, **kwargs) - - def get_form_class(self): - """ Returns the form class. """ - - return FormBuilder(self.get_form_fields()).get_form_class() - - def get_form_fields(self): - """ Returns the form fields stream_data. """ - - form_fields = self.fields.stream_data - for fn in hooks.get_hooks("construct_submission_form_fields"): - form_fields = fn(form_fields) - return form_fields - - def get_submission_class(self): - """ Returns submission class. """ - - return FormSubmission - - def process_form_submission(self, form): - """ Runs each hook if selected in the form. """ - - for fn in hooks.get_hooks("process_form_submission"): - if fn.__name__ in self.process_form_submission_hooks: - fn(self, form) - class Form(AbstractForm): pass diff --git a/wagtailstreamforms/streamfield.py b/wagtailstreamforms/streamfield.py index 07c220c7..f0a1efde 100644 --- a/wagtailstreamforms/streamfield.py +++ b/wagtailstreamforms/streamfield.py @@ -26,12 +26,20 @@ def __init__(self, local_blocks=None, **kwargs): ) # assign the block - block = field_class().get_form_block() - block.set_name(name) + block = self.instantiate_block(field_class, name) self._child_blocks[name] = block self._dependencies = self._child_blocks.values() + def instantiate_block(self, field_class, name): + """ + Provides an extension point for changing attributes of blocks, like the + meta.group + """ + block = field_class().get_form_block() + block.set_name(name) + return block + @property def child_blocks(self): return self._child_blocks From 1acf5be357c34d9ec2e92f34909663de1077d27d Mon Sep 17 00:00:00 2001 From: Ryan Sullivan Date: Mon, 14 Sep 2020 10:31:25 -0400 Subject: [PATCH 02/33] Extension point for create_field_class and some comments --- wagtailstreamforms/forms.py | 55 +++++++++++++++++-------------- wagtailstreamforms/models/form.py | 19 +++++++---- 2 files changed, 44 insertions(+), 30 deletions(-) diff --git a/wagtailstreamforms/forms.py b/wagtailstreamforms/forms.py index 798a8b7f..7612ea79 100644 --- a/wagtailstreamforms/forms.py +++ b/wagtailstreamforms/forms.py @@ -26,31 +26,8 @@ def formfields(self): formfields = OrderedDict() - registered_fields = get_fields() - for field in self.fields: - field_type = field.get("type") - field_value = field.get("value") - - # check we have the field - if field_type not in registered_fields: - raise AttributeError( - "Could not find a registered field of type %s" % field_type - ) - - # check there is a label - if "label" not in field_value: - raise AttributeError( - "The block for %s must contain a label of type blocks.CharBlock(required=True)" - % field_type - ) - - # slugify the label for the field name - field_name = get_slug_from_string(field_value.get("label")) - - # get the field - registered_cls = registered_fields[field_type]() - field_cls = registered_cls.get_formfield(field_value) + field_name, field_cls = self.create_field_class(field) formfields[field_name] = field_cls # add fields to uniquely identify the form @@ -59,6 +36,36 @@ def formfields(self): return formfields + def create_field_class(self, field): + """ + Encapsulates the field_cls creation such that there is a method to override + when the field_cls needs to be modified. + @param field: + @return: + """ + registered_fields = get_fields() + + field_type = field.get("type") + field_value = field.get("value") + # check we have the field + if field_type not in registered_fields: + raise AttributeError( + "Could not find a registered field of type %s" % field_type + ) + # check there is a label + if "label" not in field_value: + raise AttributeError( + "The block for %s must contain a label of type blocks.CharBlock(" + "required=True)" + % field_type + ) + # slugify the label for the field name + field_name = get_slug_from_string(field_value.get("label")) + # get the field + registered_cls = registered_fields[field_type]() + field_cls = registered_cls.get_formfield(field_value) + return field_name, field_cls + def get_form_class(self): return type(str("StreamformsForm"), (BaseForm,), self.formfields) diff --git a/wagtailstreamforms/models/form.py b/wagtailstreamforms/models/form.py index 3ab8b37a..26fd35d2 100644 --- a/wagtailstreamforms/models/form.py +++ b/wagtailstreamforms/models/form.py @@ -22,13 +22,11 @@ from .submission import FormSubmission -class FormQuerySet(models.QuerySet): - def for_site(self, site): - """Return all forms for a specific site.""" - return self.filter(site=site) - - class AbstractFormOuter(models.Model): + """ + Provides the fields and panels necessary for participating in form rendering and + processing. + """ submit_button_text = models.CharField( _("Submit button text"), max_length=100, default="Submit" ) @@ -118,7 +116,16 @@ class Meta: abstract = True +class FormQuerySet(models.QuerySet): + def for_site(self, site): + """Return all forms for a specific site.""" + return self.filter(site=site) + + class AbstractForm(AbstractFormOuter): + """ + Provides the fields and handlers necessary for creating reusable form objects. + """ site = models.ForeignKey(Site, on_delete=models.SET_NULL, null=True, blank=True) title = models.CharField(_("Title"), max_length=255) slug = models.SlugField( From 9068bb0a55f1f5c48d7a9ac47ad6cce36679fd69 Mon Sep 17 00:00:00 2001 From: Ryan Sullivan Date: Fri, 16 Oct 2020 08:07:52 -0400 Subject: [PATCH 03/33] Encapsulates the field_name creation such that there is a method to override when the field_name needs to be modified. --- wagtailstreamforms/forms.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/wagtailstreamforms/forms.py b/wagtailstreamforms/forms.py index 7612ea79..2936c6b8 100644 --- a/wagtailstreamforms/forms.py +++ b/wagtailstreamforms/forms.py @@ -52,6 +52,22 @@ def create_field_class(self, field): raise AttributeError( "Could not find a registered field of type %s" % field_type ) + field_name = self.create_field_name(field) + # get the field + registered_cls = registered_fields[field_type]() + field_cls = registered_cls.get_formfield(field_value) + return field_name, field_cls + + def create_field_name(self, field): + """ + Encapsulates the field_name creation such that there is a method to override + when the field_name needs to be modified. + + @param field: + @return: + """ + field_type = field.get("type") + field_value = field.get("value") # check there is a label if "label" not in field_value: raise AttributeError( @@ -60,11 +76,7 @@ def create_field_class(self, field): % field_type ) # slugify the label for the field name - field_name = get_slug_from_string(field_value.get("label")) - # get the field - registered_cls = registered_fields[field_type]() - field_cls = registered_cls.get_formfield(field_value) - return field_name, field_cls + return get_slug_from_string(field_value.get("label")) def get_form_class(self): return type(str("StreamformsForm"), (BaseForm,), self.formfields) From e893e7d37400fc8a6f91c2d5189f9fdaeb3713fa Mon Sep 17 00:00:00 2001 From: Ryan Sullivan Date: Wed, 18 Nov 2020 16:23:17 -0500 Subject: [PATCH 04/33] Undo AbstractFormOut - no longer needed --- wagtailstreamforms/models/form.py | 155 +++++++++++++----------------- 1 file changed, 68 insertions(+), 87 deletions(-) diff --git a/wagtailstreamforms/models/form.py b/wagtailstreamforms/models/form.py index 26fd35d2..2c7e8d59 100644 --- a/wagtailstreamforms/models/form.py +++ b/wagtailstreamforms/models/form.py @@ -22,11 +22,26 @@ from .submission import FormSubmission -class AbstractFormOuter(models.Model): - """ - Provides the fields and panels necessary for participating in form rendering and - processing. - """ +class FormQuerySet(models.QuerySet): + def for_site(self, site): + """Return all forms for a specific site.""" + return self.filter(site=site) + + +class AbstractForm(models.Model): + site = models.ForeignKey(Site, on_delete=models.SET_NULL, null=True, blank=True) + title = models.CharField(_("Title"), max_length=255) + slug = models.SlugField( + _("Slug"), + allow_unicode=True, + max_length=255, + unique=True, + help_text=_("Used to identify the form in template tags"), + ) + template_name = models.CharField( + _("Template"), max_length=255, choices=get_setting("FORM_TEMPLATES") + ) + fields = FormFieldsStreamField([], verbose_name=_("Fields")) submit_button_text = models.CharField( _("Submit button text"), max_length=100, default="Submit" ) @@ -35,8 +50,7 @@ class AbstractFormOuter(models.Model): blank=True, max_length=255, help_text=_( - "An optional success message to show when the form has been successfully " - "submitted" + "An optional success message to show when the form has been successfully submitted" ), ) error_message = models.CharField( @@ -60,7 +74,12 @@ class AbstractFormOuter(models.Model): verbose_name=_("Submission hooks"), blank=True ) + objects = FormQuerySet.as_manager() + settings_panels = [ + FieldPanel("title", classname="full"), + FieldPanel("slug"), + FieldPanel("template_name"), FieldPanel("submit_button_text"), MultiFieldPanel( [FieldPanel("success_message"), FieldPanel("error_message")], _("Messages") @@ -69,86 +88,6 @@ class AbstractFormOuter(models.Model): PageChooserPanel("post_redirect_page"), ] - def get_data_fields(self): - """ Returns a list of tuples with (field_name, field_label). """ - - data_fields = [("submit_time", _("Submission date"))] - data_fields += [ - (get_slug_from_string(field["value"]["label"]), field["value"]["label"]) - for field in self.get_form_fields() - ] - - return data_fields - - def get_form(self, *args, **kwargs): - """ Returns the form. """ - - form_class = self.get_form_class() - form_instance = form_class(*args, **kwargs) - return form_instance - - def get_form_class(self): - """ Returns the form class. """ - - return FormBuilder(self.get_form_fields()).get_form_class() - - def get_form_fields(self): - """ Returns the form fields stream_data. """ - - form_fields = self.fields.stream_data - for fn in hooks.get_hooks("construct_submission_form_fields"): - form_fields = fn(form_fields) - return form_fields - - def get_submission_class(self): - """ Returns submission class. """ - - return FormSubmission - - def process_form_submission(self, form): - """ Runs each hook if selected in the form. """ - - for fn in hooks.get_hooks("process_form_submission"): - if fn.__name__ in self.process_form_submission_hooks: - fn(self, form) - - class Meta: - abstract = True - - -class FormQuerySet(models.QuerySet): - def for_site(self, site): - """Return all forms for a specific site.""" - return self.filter(site=site) - - -class AbstractForm(AbstractFormOuter): - """ - Provides the fields and handlers necessary for creating reusable form objects. - """ - site = models.ForeignKey(Site, on_delete=models.SET_NULL, null=True, blank=True) - title = models.CharField(_("Title"), max_length=255) - slug = models.SlugField( - _("Slug"), - allow_unicode=True, - max_length=255, - unique=True, - help_text=_("Used to identify the form in template tags"), - ) - template_name = models.CharField( - _("Template"), max_length=255, choices=get_setting("FORM_TEMPLATES") - ) - fields = FormFieldsStreamField([], verbose_name=_("Fields")) - - objects = FormQuerySet.as_manager() - - settings_panels = [ - FieldPanel("title", classname="full"), - FieldPanel("slug"), - FieldPanel("template_name"), - *AbstractFormOuter.settings_panels, - ] - field_panels = [StreamFieldPanel("fields")] edit_handler = TabbedInterface( @@ -200,6 +139,48 @@ def copy(self): copy.alters_data = True + def get_data_fields(self): + """ Returns a list of tuples with (field_name, field_label). """ + + data_fields = [("submit_time", _("Submission date"))] + data_fields += [ + (get_slug_from_string(field["value"]["label"]), field["value"]["label"]) + for field in self.get_form_fields() + ] + + return data_fields + + def get_form(self, *args, **kwargs): + """ Returns the form. """ + + form_class = self.get_form_class() + return form_class(*args, **kwargs) + + def get_form_class(self): + """ Returns the form class. """ + + return FormBuilder(self.get_form_fields()).get_form_class() + + def get_form_fields(self): + """ Returns the form fields stream_data. """ + + form_fields = self.fields.stream_data + for fn in hooks.get_hooks("construct_submission_form_fields"): + form_fields = fn(form_fields) + return form_fields + + def get_submission_class(self): + """ Returns submission class. """ + + return FormSubmission + + def process_form_submission(self, form): + """ Runs each hook if selected in the form. """ + + for fn in hooks.get_hooks("process_form_submission"): + if fn.__name__ in self.process_form_submission_hooks: + fn(self, form) + class Form(AbstractForm): pass From 8f45527f5cf338690accfb00ca9fe8954a05bf0b Mon Sep 17 00:00:00 2001 From: Ryan Sullivan Date: Thu, 19 Nov 2020 11:55:48 -0500 Subject: [PATCH 05/33] Documentation Improvements --- wagtailstreamforms/forms.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/wagtailstreamforms/forms.py b/wagtailstreamforms/forms.py index ad0978a9..37c0d1f7 100644 --- a/wagtailstreamforms/forms.py +++ b/wagtailstreamforms/forms.py @@ -40,8 +40,11 @@ def create_field_class(self, field): """ Encapsulates the field_cls creation such that there is a method to override when the field_cls needs to be modified. - @param field: - @return: + + :param field: StreamBlock representing a form field; an item in + fields.stream_data + :return: a tuple of field_name - the name to use in the html form for this + field, and field_cls - in instantiated field class that may be added to a form """ registered_fields = get_fields() @@ -64,8 +67,11 @@ def create_field_name(self, registered_cls, field): Encapsulates the field_name creation such that there is a method to override when the field_name needs to be modified. - @param field: - @return: + :param field: StreamBlock representing a form field; an item in + fields.stream_data + :param registered_cls: The subclass of wagtailstreamforms.fields.BaseField + that defined this form field + :return: a name to use in the html form for this field """ field_name = registered_cls.get_formfield_name(field.get("value")) From 5cb1022fbe72539e6400bf388c1a71eede0a8ab1 Mon Sep 17 00:00:00 2001 From: Ryan Sullivan Date: Tue, 16 Mar 2021 11:49:48 -0400 Subject: [PATCH 06/33] Upgrade to wagtail 2.12 and fix a bug --- setup.py | 2 +- tox.ini | 1 + wagtailstreamforms/forms.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 42738bdf..7cd09a7e 100644 --- a/setup.py +++ b/setup.py @@ -17,7 +17,7 @@ ] -install_requires = ["wagtail>=2,<2.12"] +install_requires = ["wagtail>=2,<2.13"] documentation_extras = [ "sphinxcontrib-spelling>=2.3.0", diff --git a/tox.ini b/tox.ini index f7f92d76..3731e660 100644 --- a/tox.ini +++ b/tox.ini @@ -26,6 +26,7 @@ deps = wt29: wagtail>=2.9,<2.10 wt210: wagtail>=2.10,<2.11 wt211: wagtail>=2.11,<2.12 + wt212: wagtail>=2.12,<2.13 commands = diff --git a/wagtailstreamforms/forms.py b/wagtailstreamforms/forms.py index 37c0d1f7..ab6d0145 100644 --- a/wagtailstreamforms/forms.py +++ b/wagtailstreamforms/forms.py @@ -73,7 +73,7 @@ def create_field_name(self, registered_cls, field): that defined this form field :return: a name to use in the html form for this field """ - field_name = registered_cls.get_formfield_name(field.get("value")) + return registered_cls.get_formfield_name(field.get("value")) def get_form_class(self): return type(str("StreamformsForm"), (BaseForm,), self.formfields) From 9a11923bf0e3059e53e09090030d276bc8fd3c10 Mon Sep 17 00:00:00 2001 From: Ryan Sullivan Date: Wed, 19 May 2021 12:38:54 -0400 Subject: [PATCH 07/33] Wagtail 2.13 --- setup.py | 2 +- tox.ini | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 55afba11..96c34824 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ install_requires = [ - "wagtail>=2,<2.13", + "wagtail>=2,<2.14", "Unidecode>=0.04.14,<2.0", ] diff --git a/tox.ini b/tox.ini index ed47055b..e868291b 100644 --- a/tox.ini +++ b/tox.ini @@ -28,6 +28,7 @@ deps = wt210: wagtail>=2.10,<2.11 wt211: wagtail>=2.11,<2.12 wt212: wagtail>=2.12,<2.13 + wt213: wagtail>=2.13,<2.14 commands = From 3384f7c787f6d1fe47415803d96c365e6583d00a Mon Sep 17 00:00:00 2001 From: Ryan Sullivan Date: Wed, 26 May 2021 13:22:22 -0400 Subject: [PATCH 08/33] Allow instantiate_block method to return None in order to cause a block to not be added --- wagtailstreamforms/streamfield.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wagtailstreamforms/streamfield.py b/wagtailstreamforms/streamfield.py index f0a1efde..7f91feb5 100644 --- a/wagtailstreamforms/streamfield.py +++ b/wagtailstreamforms/streamfield.py @@ -25,9 +25,9 @@ def __init__(self, local_blocks=None, **kwargs): "'%s' must be a subclass of '%s'" % (field_class, BaseField) ) - # assign the block - block = self.instantiate_block(field_class, name) - self._child_blocks[name] = block + # assign the block if instantiation returns non None + if block := self.instantiate_block(field_class, name): + self._child_blocks[name] = block self._dependencies = self._child_blocks.values() From 401ecd07dc75e89c4e3c34b065061049f88ac605 Mon Sep 17 00:00:00 2001 From: Tonis Piip Date: Fri, 23 Sep 2022 14:44:22 +0300 Subject: [PATCH 09/33] clean(lint): remove unused import --- tests/fields/test_hook_select_field.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/fields/test_hook_select_field.py b/tests/fields/test_hook_select_field.py index bb40fe08..5902af41 100644 --- a/tests/fields/test_hook_select_field.py +++ b/tests/fields/test_hook_select_field.py @@ -1,6 +1,5 @@ from django.core import serializers from django.core.exceptions import ValidationError -from django.db import models from django.forms import CheckboxSelectMultiple from wagtailstreamforms.fields import HookMultiSelectFormField, HookSelectField From 2d011a25d36bf2a5035855658d0764ca0d278965 Mon Sep 17 00:00:00 2001 From: Tonis Piip Date: Fri, 23 Sep 2022 14:44:42 +0300 Subject: [PATCH 10/33] fix(ci): enable testing for WT 3 and 4 --- tox.ini | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 636a0dec..899abeb2 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] envlist = flake8 - py{37,38,39,310}-dj{32,40}-wt{215,216} + py{37,38,39,310}-dj{32,40}-wt{215,216,30,40} [gh-actions] python = @@ -18,6 +18,8 @@ deps = dj40: Django>=4.0,<4.1 wt215: wagtail>=2.15,<2.16 wt216: wagtail>=2.16,<2.17 + wt30: wagtail==3.0.* + wt40: wagtail==4.0.* commands = coverage run manage.py test From d6339a5e5eb1f1060b373704bf239f1f374d1b9e Mon Sep 17 00:00:00 2001 From: Tonis Piip Date: Fri, 23 Sep 2022 14:46:18 +0300 Subject: [PATCH 11/33] fix(example app): Have example app work, issues w/ outdated db and unrequired app in INSTALLED APPS --- docker-compose.yml | 2 +- example/settings.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 5b919e96..3410c4ab 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -24,7 +24,7 @@ services: ports: - 8000:8000 db: - image: postgres:9.6 + image: postgres:11 environment: - POSTGRES_USER=postgres - POSTGRES_PASSWORD=password diff --git a/example/settings.py b/example/settings.py index 353e19f3..1b015057 100644 --- a/example/settings.py +++ b/example/settings.py @@ -38,7 +38,6 @@ 'wagtail.contrib.redirects', 'wagtail.sites', 'wagtail.contrib.modeladmin', - 'wagtail.contrib.postgres_search', 'wagtail.contrib.settings', 'wagtail.contrib.search_promotions', From 8eca002d12f2b82a26eebb9e3dda4baf6adf0077 Mon Sep 17 00:00:00 2001 From: Tonis Piip Date: Fri, 23 Sep 2022 14:46:41 +0300 Subject: [PATCH 12/33] fix(deps) allow installing w/ version 3.0.3 of WT --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 75c57602..b0333477 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ install_requires = [ - "wagtail>=2,<2.17", + "wagtail>=2,<=3.0.3", "Unidecode>=0.04.14,<2.0", ] From d07911d115b0122292fea32f7cf7ef212fa30421 Mon Sep 17 00:00:00 2001 From: Ryan Sullivan Date: Wed, 19 Oct 2022 10:31:04 -0400 Subject: [PATCH 13/33] going after wagtail4 --- example/migrations/0001_initial.py | 6 +- example/models.py | 8 +- example/settings.py | 2 +- example/urls.py | 2 +- example/wagtailstreamforms_fields.py | 2 +- setup.py | 2 +- tests/hooks/test_process_form.py | 2 +- tests/models/test_form.py | 2 +- tests/settings.py | 2 +- tests/urls.py | 2 +- wagtailstreamforms/blocks.py | 2 +- wagtailstreamforms/fields.py | 4 +- wagtailstreamforms/migrations/0001_initial.py | 144 +++++++++--------- wagtailstreamforms/models/form.py | 4 +- wagtailstreamforms/streamfield.py | 4 +- wagtailstreamforms/wagtail_hooks.py | 2 +- .../wagtailstreamforms_fields.py | 2 +- 17 files changed, 96 insertions(+), 96 deletions(-) diff --git a/example/migrations/0001_initial.py b/example/migrations/0001_initial.py index f623c26a..ed939d89 100644 --- a/example/migrations/0001_initial.py +++ b/example/migrations/0001_initial.py @@ -2,8 +2,8 @@ from django.db import migrations, models import django.db.models.deletion -import wagtail.core.blocks -import wagtail.core.fields +import wagtail.blocks +import wagtail.fields import wagtailstreamforms.blocks @@ -20,7 +20,7 @@ class Migration(migrations.Migration): name='BasicPage', fields=[ ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.Page')), - ('body', wagtail.core.fields.StreamField((('rich_text', wagtail.core.blocks.RichTextBlock()), ('form', wagtail.core.blocks.StructBlock((('form', wagtailstreamforms.blocks.FormChooserBlock()), ('form_action', wagtail.core.blocks.CharBlock(help_text='The form post action. "" or "." for the current page or a url', required=False)), ('form_reference', wagtailstreamforms.blocks.InfoBlock(help_text='This form will be given a unique reference once saved', required=False)))))))), + ('body', wagtail.fields.StreamField((('rich_text', wagtail.blocks.RichTextBlock()), ('form', wagtail.blocks.StructBlock((('form', wagtailstreamforms.blocks.FormChooserBlock()), ('form_action', wagtail.blocks.CharBlock(help_text='The form post action. "" or "." for the current page or a url', required=False)), ('form_reference', wagtailstreamforms.blocks.InfoBlock(help_text='This form will be given a unique reference once saved', required=False)))))))), ], options={ 'abstract': False, diff --git a/example/models.py b/example/models.py index e0839692..9bc38212 100644 --- a/example/models.py +++ b/example/models.py @@ -1,9 +1,9 @@ from django.db import models -from wagtail.admin.edit_handlers import StreamFieldPanel -from wagtail.core import blocks -from wagtail.core.fields import StreamField -from wagtail.core.models import Page +from wagtail.admin.panels import StreamFieldPanel +from wagtail import blocks +from wagtail.fields import StreamField +from wagtail.models import Page from wagtailstreamforms.blocks import WagtailFormBlock from wagtailstreamforms.models.abstract import AbstractFormSetting diff --git a/example/settings.py b/example/settings.py index 1b015057..946ed33f 100644 --- a/example/settings.py +++ b/example/settings.py @@ -27,7 +27,7 @@ 'django.contrib.staticfiles', # wagtail - 'wagtail.core', + 'wagtail', 'wagtail.admin', 'wagtail.documents', 'wagtail.snippets', diff --git a/example/urls.py b/example/urls.py index 1a6397cf..de13e671 100644 --- a/example/urls.py +++ b/example/urls.py @@ -4,7 +4,7 @@ from django.contrib import admin from wagtail.admin import urls as wagtailadmin_urls -from wagtail.core import urls as wagtail_urls +from wagtail import urls as wagtail_urls from wagtail.documents import urls as wagtaildocs_urls diff --git a/example/wagtailstreamforms_fields.py b/example/wagtailstreamforms_fields.py index 28c57445..73d5c5b5 100644 --- a/example/wagtailstreamforms_fields.py +++ b/example/wagtailstreamforms_fields.py @@ -2,7 +2,7 @@ from django.contrib.auth.models import User from captcha.fields import ReCaptchaField -from wagtail.core import blocks +from wagtail import blocks from wagtailstreamforms.fields import BaseField, register diff --git a/setup.py b/setup.py index b0333477..fb9df5c8 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ install_requires = [ - "wagtail>=2,<=3.0.3", + "wagtail>=2,<=4.1", "Unidecode>=0.04.14,<2.0", ] diff --git a/tests/hooks/test_process_form.py b/tests/hooks/test_process_form.py index 2ba91fa8..781bf4c1 100644 --- a/tests/hooks/test_process_form.py +++ b/tests/hooks/test_process_form.py @@ -2,7 +2,7 @@ from django.test import override_settings from django.test.client import Client from mock import patch -from wagtail.core.models import Page +from wagtail.models import Page from wagtailstreamforms.models import Form from wagtailstreamforms.wagtail_hooks import process_form diff --git a/tests/models/test_form.py b/tests/models/test_form.py index 11a9db0a..273253ad 100644 --- a/tests/models/test_form.py +++ b/tests/models/test_form.py @@ -2,7 +2,7 @@ from django.db import models from django.test import override_settings from django.utils.translation import gettext_lazy as _ -from wagtail.core.models import Page +from wagtail.models import Page from wagtailstreamforms.conf import get_setting from wagtailstreamforms.fields import HookSelectField diff --git a/tests/settings.py b/tests/settings.py index 8fdc71f4..d0bcbd4b 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -14,7 +14,7 @@ "django.contrib.messages", "django.contrib.staticfiles", # wagtail - "wagtail.core", + "wagtail", "wagtail.admin", "wagtail.documents", "wagtail.snippets", diff --git a/tests/urls.py b/tests/urls.py index 65c654f4..10877f70 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -1,7 +1,7 @@ from django.contrib import admin from django.urls import include, re_path from wagtail.admin import urls as wagtailadmin_urls -from wagtail.core import urls as wagtail_urls +from wagtail import urls as wagtail_urls urlpatterns = [ re_path(r"^admin/", admin.site.urls), diff --git a/wagtailstreamforms/blocks.py b/wagtailstreamforms/blocks.py index c314028d..40b61976 100644 --- a/wagtailstreamforms/blocks.py +++ b/wagtailstreamforms/blocks.py @@ -3,7 +3,7 @@ from django import forms from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy as _ -from wagtail.core import blocks +from wagtail import blocks from wagtailstreamforms.models import Form diff --git a/wagtailstreamforms/fields.py b/wagtailstreamforms/fields.py index cc0a37b4..8b0a22bd 100644 --- a/wagtailstreamforms/fields.py +++ b/wagtailstreamforms/fields.py @@ -2,7 +2,7 @@ from django.core import exceptions from django.db import models from django.utils.text import capfirst -from wagtail.core import blocks +from wagtail import blocks from wagtailstreamforms import hooks from wagtailstreamforms.utils.apps import get_app_submodules @@ -152,7 +152,7 @@ def get_form_block(self): Override this to provide additional fields in the StreamField. - :return: The ``wagtail.core.blocks.StructBlock`` to be used in the StreamField + :return: The ``wagtail.blocks.StructBlock`` to be used in the StreamField """ return blocks.StructBlock( [ diff --git a/wagtailstreamforms/migrations/0001_initial.py b/wagtailstreamforms/migrations/0001_initial.py index e68b1639..e2f7c440 100644 --- a/wagtailstreamforms/migrations/0001_initial.py +++ b/wagtailstreamforms/migrations/0001_initial.py @@ -1,7 +1,7 @@ # Generated by Django 2.0.5 on 2018-05-30 23:03 import django.db.models.deletion -import wagtail.core.blocks +import wagtail.blocks from django.db import migrations, models import wagtailstreamforms.conf @@ -53,24 +53,24 @@ class Migration(migrations.Migration): [ ( "singleline", - wagtail.core.blocks.StructBlock( + wagtail.blocks.StructBlock( [ - ("label", wagtail.core.blocks.CharBlock()), + ("label", wagtail.blocks.CharBlock()), ( "help_text", - wagtail.core.blocks.CharBlock( + wagtail.blocks.CharBlock( required=False ), ), ( "required", - wagtail.core.blocks.BooleanBlock( + wagtail.blocks.BooleanBlock( required=False ), ), ( "default_value", - wagtail.core.blocks.CharBlock( + wagtail.blocks.CharBlock( required=False ), ), @@ -81,24 +81,24 @@ class Migration(migrations.Migration): ), ( "multiline", - wagtail.core.blocks.StructBlock( + wagtail.blocks.StructBlock( [ - ("label", wagtail.core.blocks.CharBlock()), + ("label", wagtail.blocks.CharBlock()), ( "help_text", - wagtail.core.blocks.CharBlock( + wagtail.blocks.CharBlock( required=False ), ), ( "required", - wagtail.core.blocks.BooleanBlock( + wagtail.blocks.BooleanBlock( required=False ), ), ( "default_value", - wagtail.core.blocks.CharBlock( + wagtail.blocks.CharBlock( required=False ), ), @@ -109,24 +109,24 @@ class Migration(migrations.Migration): ), ( "date", - wagtail.core.blocks.StructBlock( + wagtail.blocks.StructBlock( [ - ("label", wagtail.core.blocks.CharBlock()), + ("label", wagtail.blocks.CharBlock()), ( "help_text", - wagtail.core.blocks.CharBlock( + wagtail.blocks.CharBlock( required=False ), ), ( "required", - wagtail.core.blocks.BooleanBlock( + wagtail.blocks.BooleanBlock( required=False ), ), ( "default_value", - wagtail.core.blocks.CharBlock( + wagtail.blocks.CharBlock( required=False ), ), @@ -137,24 +137,24 @@ class Migration(migrations.Migration): ), ( "datetime", - wagtail.core.blocks.StructBlock( + wagtail.blocks.StructBlock( [ - ("label", wagtail.core.blocks.CharBlock()), + ("label", wagtail.blocks.CharBlock()), ( "help_text", - wagtail.core.blocks.CharBlock( + wagtail.blocks.CharBlock( required=False ), ), ( "required", - wagtail.core.blocks.BooleanBlock( + wagtail.blocks.BooleanBlock( required=False ), ), ( "default_value", - wagtail.core.blocks.CharBlock( + wagtail.blocks.CharBlock( required=False ), ), @@ -165,24 +165,24 @@ class Migration(migrations.Migration): ), ( "email", - wagtail.core.blocks.StructBlock( + wagtail.blocks.StructBlock( [ - ("label", wagtail.core.blocks.CharBlock()), + ("label", wagtail.blocks.CharBlock()), ( "help_text", - wagtail.core.blocks.CharBlock( + wagtail.blocks.CharBlock( required=False ), ), ( "required", - wagtail.core.blocks.BooleanBlock( + wagtail.blocks.BooleanBlock( required=False ), ), ( "default_value", - wagtail.core.blocks.CharBlock( + wagtail.blocks.CharBlock( required=False ), ), @@ -193,24 +193,24 @@ class Migration(migrations.Migration): ), ( "url", - wagtail.core.blocks.StructBlock( + wagtail.blocks.StructBlock( [ - ("label", wagtail.core.blocks.CharBlock()), + ("label", wagtail.blocks.CharBlock()), ( "help_text", - wagtail.core.blocks.CharBlock( + wagtail.blocks.CharBlock( required=False ), ), ( "required", - wagtail.core.blocks.BooleanBlock( + wagtail.blocks.BooleanBlock( required=False ), ), ( "default_value", - wagtail.core.blocks.CharBlock( + wagtail.blocks.CharBlock( required=False ), ), @@ -221,24 +221,24 @@ class Migration(migrations.Migration): ), ( "number", - wagtail.core.blocks.StructBlock( + wagtail.blocks.StructBlock( [ - ("label", wagtail.core.blocks.CharBlock()), + ("label", wagtail.blocks.CharBlock()), ( "help_text", - wagtail.core.blocks.CharBlock( + wagtail.blocks.CharBlock( required=False ), ), ( "required", - wagtail.core.blocks.BooleanBlock( + wagtail.blocks.BooleanBlock( required=False ), ), ( "default_value", - wagtail.core.blocks.CharBlock( + wagtail.blocks.CharBlock( required=False ), ), @@ -249,31 +249,31 @@ class Migration(migrations.Migration): ), ( "dropdown", - wagtail.core.blocks.StructBlock( + wagtail.blocks.StructBlock( [ - ("label", wagtail.core.blocks.CharBlock()), + ("label", wagtail.blocks.CharBlock()), ( "help_text", - wagtail.core.blocks.CharBlock( + wagtail.blocks.CharBlock( required=False ), ), ( "required", - wagtail.core.blocks.BooleanBlock( + wagtail.blocks.BooleanBlock( required=False ), ), ( "empty_label", - wagtail.core.blocks.CharBlock( + wagtail.blocks.CharBlock( required=False ), ), ( "choices", - wagtail.core.blocks.ListBlock( - wagtail.core.blocks.CharBlock( + wagtail.blocks.ListBlock( + wagtail.blocks.CharBlock( label="Option" ) ), @@ -285,25 +285,25 @@ class Migration(migrations.Migration): ), ( "radio", - wagtail.core.blocks.StructBlock( + wagtail.blocks.StructBlock( [ - ("label", wagtail.core.blocks.CharBlock()), + ("label", wagtail.blocks.CharBlock()), ( "help_text", - wagtail.core.blocks.CharBlock( + wagtail.blocks.CharBlock( required=False ), ), ( "required", - wagtail.core.blocks.BooleanBlock( + wagtail.blocks.BooleanBlock( required=False ), ), ( "choices", - wagtail.core.blocks.ListBlock( - wagtail.core.blocks.CharBlock( + wagtail.blocks.ListBlock( + wagtail.blocks.CharBlock( label="Option" ) ), @@ -315,25 +315,25 @@ class Migration(migrations.Migration): ), ( "checkboxes", - wagtail.core.blocks.StructBlock( + wagtail.blocks.StructBlock( [ - ("label", wagtail.core.blocks.CharBlock()), + ("label", wagtail.blocks.CharBlock()), ( "help_text", - wagtail.core.blocks.CharBlock( + wagtail.blocks.CharBlock( required=False ), ), ( "required", - wagtail.core.blocks.BooleanBlock( + wagtail.blocks.BooleanBlock( required=False ), ), ( "choices", - wagtail.core.blocks.ListBlock( - wagtail.core.blocks.CharBlock( + wagtail.blocks.ListBlock( + wagtail.blocks.CharBlock( label="Option" ) ), @@ -345,18 +345,18 @@ class Migration(migrations.Migration): ), ( "checkbox", - wagtail.core.blocks.StructBlock( + wagtail.blocks.StructBlock( [ - ("label", wagtail.core.blocks.CharBlock()), + ("label", wagtail.blocks.CharBlock()), ( "help_text", - wagtail.core.blocks.CharBlock( + wagtail.blocks.CharBlock( required=False ), ), ( "required", - wagtail.core.blocks.BooleanBlock( + wagtail.blocks.BooleanBlock( required=False ), ), @@ -367,24 +367,24 @@ class Migration(migrations.Migration): ), ( "hidden", - wagtail.core.blocks.StructBlock( + wagtail.blocks.StructBlock( [ - ("label", wagtail.core.blocks.CharBlock()), + ("label", wagtail.blocks.CharBlock()), ( "help_text", - wagtail.core.blocks.CharBlock( + wagtail.blocks.CharBlock( required=False ), ), ( "required", - wagtail.core.blocks.BooleanBlock( + wagtail.blocks.BooleanBlock( required=False ), ), ( "default_value", - wagtail.core.blocks.CharBlock( + wagtail.blocks.CharBlock( required=False ), ), @@ -395,18 +395,18 @@ class Migration(migrations.Migration): ), ( "singlefile", - wagtail.core.blocks.StructBlock( + wagtail.blocks.StructBlock( [ - ("label", wagtail.core.blocks.CharBlock()), + ("label", wagtail.blocks.CharBlock()), ( "help_text", - wagtail.core.blocks.CharBlock( + wagtail.blocks.CharBlock( required=False ), ), ( "required", - wagtail.core.blocks.BooleanBlock( + wagtail.blocks.BooleanBlock( required=False ), ), @@ -417,18 +417,18 @@ class Migration(migrations.Migration): ), ( "multifile", - wagtail.core.blocks.StructBlock( + wagtail.blocks.StructBlock( [ - ("label", wagtail.core.blocks.CharBlock()), + ("label", wagtail.blocks.CharBlock()), ( "help_text", - wagtail.core.blocks.CharBlock( + wagtail.blocks.CharBlock( required=False ), ), ( "required", - wagtail.core.blocks.BooleanBlock( + wagtail.blocks.BooleanBlock( required=False ), ), diff --git a/wagtailstreamforms/models/form.py b/wagtailstreamforms/models/form.py index c9f06314..3eaf9a23 100644 --- a/wagtailstreamforms/models/form.py +++ b/wagtailstreamforms/models/form.py @@ -3,7 +3,7 @@ from django.db import models from django.utils.translation import gettext_lazy as _ from wagtail import VERSION as WAGTAIL_VERSION -from wagtail.admin.edit_handlers import ( +from wagtail.admin.panels import ( FieldPanel, MultiFieldPanel, ObjectList, @@ -11,7 +11,7 @@ StreamFieldPanel, TabbedInterface, ) -from wagtail.core.models import Site +from wagtail.models import Site from wagtailstreamforms import hooks from wagtailstreamforms.conf import get_setting diff --git a/wagtailstreamforms/streamfield.py b/wagtailstreamforms/streamfield.py index c7804138..5307780e 100644 --- a/wagtailstreamforms/streamfield.py +++ b/wagtailstreamforms/streamfield.py @@ -1,6 +1,6 @@ from django.core.exceptions import ImproperlyConfigured -from wagtail.core import blocks -from wagtail.core.fields import StreamField +from wagtail import blocks +from wagtail.fields import StreamField from wagtailstreamforms.fields import BaseField, get_fields diff --git a/wagtailstreamforms/wagtail_hooks.py b/wagtailstreamforms/wagtail_hooks.py index 17074e8f..f7e3e99d 100644 --- a/wagtailstreamforms/wagtail_hooks.py +++ b/wagtailstreamforms/wagtail_hooks.py @@ -15,7 +15,7 @@ EditView, InspectView, ) -from wagtail.core import hooks +from wagtail import hooks from wagtailstreamforms import hooks as form_hooks from wagtailstreamforms.conf import get_setting diff --git a/wagtailstreamforms/wagtailstreamforms_fields.py b/wagtailstreamforms/wagtailstreamforms_fields.py index 4348ca24..d76c7ad4 100644 --- a/wagtailstreamforms/wagtailstreamforms_fields.py +++ b/wagtailstreamforms/wagtailstreamforms_fields.py @@ -2,7 +2,7 @@ from django import forms from django.utils.translation import gettext_lazy as _ -from wagtail.core import blocks +from wagtail import blocks from wagtailstreamforms.conf import get_setting from wagtailstreamforms.fields import BaseField, register From 883fc2b4877a47156aa958712c5fedeaece0d9d6 Mon Sep 17 00:00:00 2001 From: Ryan Sullivan Date: Tue, 25 Oct 2022 13:02:54 -0400 Subject: [PATCH 14/33] Require Wagtail 3 or higher for package path changes --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index fb9df5c8..430e6a94 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ install_requires = [ - "wagtail>=2,<=4.1", + "wagtail>=3,<=4.1", "Unidecode>=0.04.14,<2.0", ] From 66d84022bd96975a468f279a79ced20c23fd7cc5 Mon Sep 17 00:00:00 2001 From: Ryan Sullivan Date: Tue, 25 Oct 2022 13:17:22 -0400 Subject: [PATCH 15/33] Removes StreamFieldPanel and adds use_json_field=True to instantiation of FormFieldsStreamField --- .../migrations/0003_alter_form_fields.py | 25 +++++++++++++++++++ wagtailstreamforms/models/form.py | 6 ++--- 2 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 wagtailstreamforms/migrations/0003_alter_form_fields.py diff --git a/wagtailstreamforms/migrations/0003_alter_form_fields.py b/wagtailstreamforms/migrations/0003_alter_form_fields.py new file mode 100644 index 00000000..7a768306 --- /dev/null +++ b/wagtailstreamforms/migrations/0003_alter_form_fields.py @@ -0,0 +1,25 @@ +# Generated by Django 4.0.8 on 2022-10-25 17:11 + +import cms.query_forms.blocks +import cms.query_forms.validators +import cms.utils.blocks +import data_dictionary.models +from django.db import migrations +import wagtail.blocks +import wagtail.blocks.static_block +import wagtailstreamforms.streamfield + + +class Migration(migrations.Migration): + + dependencies = [ + ('wagtailstreamforms', '0002_form_site'), + ] + + operations = [ + migrations.AlterField( + model_name='form', + name='fields', + field=wagtailstreamforms.streamfield.FormFieldsStreamField([('singleline', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock()), ('help_text', wagtail.blocks.CharBlock(required=False)), ('required', wagtail.blocks.BooleanBlock(required=False)), ('default_value', wagtail.blocks.CharBlock(required=False))], icon='placeholder', label='Text field (single line)')), ('multiline', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock()), ('help_text', wagtail.blocks.CharBlock(required=False)), ('required', wagtail.blocks.BooleanBlock(required=False)), ('default_value', wagtail.blocks.CharBlock(required=False))], icon='placeholder', label='Text field (multi line)')), ('date', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock()), ('help_text', wagtail.blocks.CharBlock(required=False)), ('required', wagtail.blocks.BooleanBlock(required=False)), ('default_value', wagtail.blocks.CharBlock(required=False))], icon='date', label='Date field')), ('datetime', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock()), ('help_text', wagtail.blocks.CharBlock(required=False)), ('required', wagtail.blocks.BooleanBlock(required=False)), ('default_value', wagtail.blocks.CharBlock(required=False))], icon='time', label='DateTime field')), ('email', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock()), ('help_text', wagtail.blocks.CharBlock(required=False)), ('required', wagtail.blocks.BooleanBlock(required=False)), ('default_value', wagtail.blocks.CharBlock(required=False))], icon='mail', label='Email field')), ('url', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock()), ('help_text', wagtail.blocks.CharBlock(required=False)), ('required', wagtail.blocks.BooleanBlock(required=False)), ('default_value', wagtail.blocks.CharBlock(required=False))], icon='link', label='URL field')), ('number', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock()), ('help_text', wagtail.blocks.CharBlock(required=False)), ('required', wagtail.blocks.BooleanBlock(required=False)), ('default_value', wagtail.blocks.CharBlock(required=False))], icon='placeholder', label='Number field')), ('dropdown', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock()), ('help_text', wagtail.blocks.CharBlock(required=False)), ('required', wagtail.blocks.BooleanBlock(required=False)), ('empty_label', wagtail.blocks.CharBlock(required=False)), ('choices_source', wagtail.blocks.StreamBlock([('choices', wagtail.blocks.ListBlock(wagtail.blocks.StructBlock([('value', wagtail.blocks.CharBlock(label='Option Value')), ('label', wagtail.blocks.CharBlock(label='Option Label')), ('selected', wagtail.blocks.BooleanBlock(help_text='Default this dropdown item to be selected. While you can mark multiple items selected, only the last one will be selected on the form. If no items are marked selected then the first, or the empty label, will be selected on the form.', required=False))]))), ('table_choices', cms.query_forms.blocks.TableChooserBlock(help_text='A Table from which variable will be used as choices. The Table choices are limited to those tables that exist within the Product selected on this Query Form and are not deleted. If the table becomes deleted then all variables within it will be automatically hidden from choices.'))], help_text='Choices from all sources are aggregated and sorted'))], icon='arrow-down-big', label='Dropdown field')), ('radio', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock()), ('help_text', wagtail.blocks.CharBlock(required=False)), ('required', wagtail.blocks.BooleanBlock(required=False)), ('choices', wagtail.blocks.ListBlock(wagtail.blocks.StructBlock([('value', wagtail.blocks.CharBlock(label='Option Value')), ('label', wagtail.blocks.CharBlock(label='Option Label')), ('selected', wagtail.blocks.BooleanBlock(help_text='Default this radio button to be selected. While you can mark multiple radio buttons selected, only the last one will be selected on the form. If no radios are marked selected then the first will be selected on the form.', required=False)), ('help_modal', cms.query_forms.blocks.ModalSnippetChooserBlock(help_text="Additional information that will be shown in a modal. The radio item will become a link to show a modal and the ModalSnippet's link text and title will not be used.", required=False))])))], icon='radio-empty', label='Radio buttons')), ('checkboxes', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock()), ('help_text', wagtail.blocks.CharBlock(required=False)), ('required', wagtail.blocks.BooleanBlock(required=False)), ('required_min', wagtail.blocks.IntegerBlock(help_text='When required, the minimum number of boxes that may be checked, or -1 to disable this functionality', min_value=-1, required=False)), ('required_max', wagtail.blocks.IntegerBlock(help_text='When required, the maximum number of boxes that may be checked, or -1 to disable this functionality', min_value=-1, required=False)), ('layout', wagtail.blocks.ChoiceBlock(choices=[('default', 'Default'), ('horizontal', 'Horizontal')], label='Checkboxes Layout')), ('choices', wagtail.blocks.ListBlock(wagtail.blocks.StructBlock([('value', wagtail.blocks.CharBlock(label='Option Value')), ('label', wagtail.blocks.CharBlock(label='Option Label')), ('selected', wagtail.blocks.BooleanBlock(required=False)), ('help_modal', cms.query_forms.blocks.ModalSnippetChooserBlock(help_text="Additional information that will be shown in a modal. The radio item will become a link to show a modal and the ModalSnippet's link text and title will not be used.", required=False))])))], icon='tick-inverse', label='Checkboxes')), ('checkbox', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock()), ('help_text', wagtail.blocks.CharBlock(required=False)), ('required', wagtail.blocks.BooleanBlock(required=False))], icon='tick-inverse', label='Checkbox field')), ('hidden', wagtail.blocks.StructBlock([('hidden_fields_block', wagtail.blocks.StreamBlock([('hidden_field', wagtail.blocks.StructBlock([('form_field_name', wagtail.blocks.CharBlock(help_text='Field name used as the key, in the payload, for the values entered by the user, when submitting the resulting Query to Query Manager.')), ('value', wagtail.blocks.CharBlock(help_text='The value associated with this hidden field. The user cannot change this value', required=True))]))]))], icon='placeholder', label='Hidden Fields')), ('singlefile', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock()), ('help_text', wagtail.blocks.CharBlock(required=False)), ('required', wagtail.blocks.BooleanBlock(required=False))], icon='doc-full-inverse', label='File field')), ('multifile', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock()), ('help_text', wagtail.blocks.CharBlock(required=False)), ('required', wagtail.blocks.BooleanBlock(required=False))], icon='doc-full-inverse', label='Files field')), ('date_range_step', wagtail.blocks.StructBlock([('form_field_name', wagtail.blocks.CharBlock(default='datevar', help_text='Field name used as the key, in the payload, for the values entered by the user, when submitting the resulting Query to Query Manager.')), ('label', wagtail.blocks.CharBlock()), ('date_range_granularity', wagtail.blocks.ChoiceBlock(choices=[('days', 'Days, YYYY-MM-DD'), ('months', 'Months, YYYY-MM'), ('quarters', 'Quarters, YYYY-MM'), ('years', 'Years, YYYY')], help_text='Applies to Date; ignored for Datetime which always requires Day granularity.', label='What is the date granularity?')), ('date_range_block', wagtail.blocks.StreamBlock([('date_variables', wagtail.blocks.StreamBlock([('date_variable_option_set', wagtail.blocks.StructBlock([('temporal_variable', cms.query_forms.blocks.VariableDateCacheChooserBlock(help_text='Date Cache object. This date variable will not be affected by the deleted state of the associated variable. However, if a the Variable Date Cache object itself is removed from Data Dictionary, then this date variable will be automatically hidden on the form.', label='Date Cache Object')), ('default_start_choice', wagtail.blocks.ChoiceBlock(choices=[('default_to_limit', 'Use Cache Default Date'), ('default_to_current', 'Use Today'), ('default_to_other', 'Use Supplied Date')], label='Which date should be used as the default start date?')), ('default_start', wagtail.blocks.DateBlock(help_text='Optionally override the default start date from the cache', label='Supplied Default Start Date', required=False)), ('default_end_choice', wagtail.blocks.ChoiceBlock(choices=[('default_to_limit', 'Use Cache Default Date'), ('default_to_current', 'Use Today'), ('default_to_other', 'Use Supplied Date')], label='Which date should be used as the default end date?')), ('default_end', wagtail.blocks.DateBlock(help_text='Optionally override the default end date from the cache', label='Supplied Default End Date', required=False)), ('max_range_seconds', wagtail.blocks.IntegerBlock(help_text='Optionally, a number of seconds that is the maximum range the user may select. Blank and values less than 1 are interpreted as an unbound range. The max possible constrained range is ~68 years.', label='Max Selectable Range in Seconds', required=False))])), ('arbitrary_variable_option_set', wagtail.blocks.StructBlock([('variable_code', wagtail.blocks.CharBlock(help_text='The code for an arbitrary date variable.', label='Date Variable Code', required=True)), ('label', wagtail.blocks.CharBlock(help_text='Optional, the label for an arbitrary date variable. If left empty, the Date Variable Code will be used.', label='Date Variable Label', required=False)), ('min_temporal', wagtail.blocks.DateBlock(label='Minimum Date', required=True)), ('max_temporal', wagtail.blocks.DateBlock(label='Maximum Date', required=True)), ('default_start_choice', wagtail.blocks.ChoiceBlock(choices=[('default_to_current', 'Use Today'), ('default_to_other', 'Use Supplied Date')], label='Which date should be used as the default start date?')), ('default_start', wagtail.blocks.DateBlock(help_text='Optionally supply the default start date', label='Supplied Default Start Date', required=False)), ('default_end_choice', wagtail.blocks.ChoiceBlock(choices=[('default_to_current', 'Use Today'), ('default_to_other', 'Use Supplied Date')], label='Which date should be used as the default end date?')), ('default_end', wagtail.blocks.DateBlock(help_text='Optionally supply the default end date', label='Supplied Default End Date', required=False)), ('max_range_seconds', wagtail.blocks.IntegerBlock(help_text='Optionally, a number of seconds that is the maximum range the user may select. Blank and values less than 1 are interpreted as an unbound range. The max possible constrained range is ~68 years.', label='Max Selectable Range in Seconds', required=False))]))])), ('datetime_variables', wagtail.blocks.StreamBlock([('date_variable_option_set', wagtail.blocks.StructBlock([('temporal_variable', cms.query_forms.blocks.VariableDateTimeCacheChooserBlock(help_text='Date/time Cache object. This date/time variable will not be affected by the deleted state of the associated variable. However, if a the Variable Date/time Cache object itself is removed from Data Dictionary, then this date/time variable will be automatically hidden on the form.', label='Date/time Cache Object')), ('default_start_choice', wagtail.blocks.ChoiceBlock(choices=[('default_to_limit', 'Use Cache Default Date/time'), ('default_to_current', 'Use Now'), ('default_to_other', 'Use Supplied Date/time')], label='Which date/time should be used as the default start date/time?')), ('default_start', wagtail.blocks.DateTimeBlock(help_text='Optionally override the default start date/time from the cache', label='Supplied Default Start Date/time', required=False)), ('default_end_choice', wagtail.blocks.ChoiceBlock(choices=[('default_to_limit', 'Use Cache Default Date/time'), ('default_to_current', 'Use Now'), ('default_to_other', 'Use Supplied Date/time')], label='Which date/time should be used as the default end date/time?')), ('default_end', wagtail.blocks.DateTimeBlock(help_text='Optionally override the default end date/time from the cache', label='Supplied Default End Date/time', required=False)), ('max_range_seconds', wagtail.blocks.IntegerBlock(help_text='Optionally, a number of seconds that is the maximum range the user may select. Blank and values less than 1 are interpreted as an unbound range. The max possible constrained range is ~68 years.', label='Max Selectable Range in Seconds', required=False))])), ('arbitrary_variable_option_set', wagtail.blocks.StructBlock([('variable_code', wagtail.blocks.CharBlock(help_text='The code for an arbitrary date/time variable.', label='Date/time Variable Code', required=True)), ('label', wagtail.blocks.CharBlock(help_text='Optional, the label for an arbitrary date variable. If left empty, the Date/time Variable Code will be used.', label='Date/time Variable Label', required=False)), ('min_temporal', wagtail.blocks.DateTimeBlock(label='Minimum Date/time', required=True)), ('max_temporal', wagtail.blocks.DateTimeBlock(label='Maximum Date/time', required=True)), ('default_start_choice', wagtail.blocks.ChoiceBlock(choices=[('default_to_current', 'Use Now'), ('default_to_other', 'Use Supplied Date/time')], label='Which date/time should be used as the default start date/time?')), ('default_start', wagtail.blocks.DateTimeBlock(help_text='Optionally supply the default start date/time', label='Supplied Default Start Date/time', required=False)), ('default_end_choice', wagtail.blocks.ChoiceBlock(choices=[('default_to_current', 'Use Now'), ('default_to_other', 'Use Supplied Date/time')], label='Which date/time should be used as the default end date/time?')), ('default_end', wagtail.blocks.DateTimeBlock(help_text='Optionally supply the default end date/time', label='Supplied Default End Date/time', required=False)), ('max_range_seconds', wagtail.blocks.IntegerBlock(help_text='Optionally, a number of seconds that is the maximum range the user may select. Blank and values less than 1 are interpreted as an unbound range. The max possible constrained range is ~68 years.', label='Max Selectable Range in Seconds', required=False))]))]))], max_num=1, min_num=1, required=True))], icon='placeholder', label='Date Range Step')), ('code_entry_step', wagtail.blocks.StructBlock([('code_entry_block', wagtail.blocks.StreamBlock([('supported_code_types', wagtail.blocks.StructBlock([('code_types_header', wagtail.blocks.CharBlock(default='What format are your company codes?', help_text='This is the text that will be shown at the top of this step.', required=False)), ('query_var_name', wagtail.blocks.CharBlock(default='qvar', help_text='The name of the main query variable to pass to SAS.', required=True)), ('code_type_options', wagtail.blocks.StreamBlock([('code_type', wagtail.blocks.StructBlock([('code_type_name', wagtail.blocks.CharBlock(help_text='The value that will be passed to SAS if this option is chosen as the main query variable.', required=True)), ('code_type_display_name', wagtail.blocks.CharBlock(help_text="The text to display for this option in the set of radio buttons. If not set, the code type's name will be used.", required=False)), ('code_attribute_type', cms.utils.blocks.SnippetChooserNoEditBlock(data_dictionary.models.AttributeType, help_text='The common attribute type associated with this code. This field is optional, but must be selected for this code type to participate in attribute search.', required=False)), ('validation', wagtail.blocks.StreamBlock([('validate_words_and_spaces', wagtail.blocks.static_block.StaticBlock(admin_text='Words may consist of characters a-z, A-Z, 0-9, andunderscore', error_message='Please enter codes that are words (a-z, A-Z, 0-9, underscore) separated by spaces.', label='Allow words and spaces', regex_manual='^\\w+( +\\w+)*$', regex_upload='^\\w+$')), ('validate_text_and_spaces', wagtail.blocks.static_block.StaticBlock(admin_text='Text may consist of characters a-z and A-Z', error_message='Please enter codes that are text (a-z, A-Z) separated by spaces.', label='Allow text and spaces', regex_manual='^[a-zA-Z]+( +[a-zA-Z]+)*$', regex_upload='^[a-zA-Z]+$')), ('validate_numbers_and_spaces', wagtail.blocks.static_block.StaticBlock(admin_text='Numbers are 0-9', error_message='Please enter codes that are numbers (0-9) separated by spaces.', label='Allow numbers and spaces', regex_manual='^[0-9]+( +[0-9]+)*$', regex_upload='^[0-9]+$')), ('validate_tickers_and_spaces', wagtail.blocks.static_block.StaticBlock(admin_text="Tickers, as currently implemented, may consist of characters a-z, A-Z, 0-9, ., :, and \\. This definition of 'Ticker' may change at a later time; if you require an immutable definition of 'Ticker', please consider using a custom regular expression instead.", error_message='Please enter codes that contains valid ticker characters (a-z, A-Z, 0-9, ., :, and \\) separated by spaces.', label='Allow tickers and spaces', regex_manual='^[0-9a-zA-Z:\\\\\\.]+( +[0-9a-zA-Z:\\\\\\.]+)*$', regex_upload='^[0-9a-zA-Z:\\\\\\.]+$')), ('validate_custom_regex', wagtail.blocks.StructBlock([('manual_entry_regex', wagtail.blocks.CharBlock(default='^\\w+( +\\w+)*$', help_text="A valid regular expression with which to validate manually entered codes. The default, ^\\w+( +\\w+)*$, will allow only words and spaces. To allow only digits and spaces, try ^\\d+( +\\d+)*$.", required=True, validators=(cms.query_forms.validators.RegexStringValidator(),))), ('file_upload_regex', wagtail.blocks.CharBlock(default='^\\w+$', help_text="A valid regular expression with which to validate codes within uploaded files. The default, ^\\w+$, will allow only zero or one words per line. To allow only zero or one integers, try ^\\d+$.", required=True, validators=(cms.query_forms.validators.RegexStringValidator(),))), ('custom_regex_error_message', wagtail.blocks.CharBlock(default='Please enter codes that are words (a-z, A-Z, 0-9, and underscore) separated by spaces.', help_text='The message to display to the user when their input has violated the custom regex.', required=False))], label='Supply a custom regular expression.'))], help_text='Optionally, impose validation on the entered or uploaded codes.', max_num=1, min_num=0, required=False))]))], required=True))])), ('supported_code_entry_methods', wagtail.blocks.StructBlock([('entry_methods_header', wagtail.blocks.CharBlock(default='Select an option for entering your company codes:', help_text='This is the text that will be shown above the set of radio boxes where the user chooses their code input method.', required=False)), ('allow_manual_entry', wagtail.blocks.BooleanBlock(default=True, help_text='Check this to create a box for manual code entry.', required=False)), ('manual_entry_label', wagtail.blocks.CharBlock(default='Company Codes', help_text='The label the field for screen readers and to be used when referring to the field in error messaging. This value will also be used as the placeholder text, visible in the manual code entry box until the user starts typing .', required=False)), ('manual_entry_caption', wagtail.blocks.CharBlock(default='Please enter company codes separated by a space.', help_text='The caption to put under the manual code entry box.', required=False)), ('code_examples', wagtail.blocks.RichTextBlock(default='Example: IBM MSFT AAPL', features=['bold', 'italic', 'ol', 'ul', 'link'], help_text='Text to use as an example of manual code entry. Should ', required=False)), ('code_lookup_by_form_product', wagtail.blocks.BooleanBlock(default=True, help_text='If a code lookup link should be rendered for the product selected for with this form.', required=False)), ('additional_code_lookups', wagtail.blocks.StreamBlock([('code_lookup_table', cms.query_forms.blocks.TableChooserBlock(help_text='The Table from which a code lookup link should be created. The Table choices are limited to those tables that exist within the Product selected on this Query Form and are not deleted. If the table becomes deleted, this code lookup will will be automatically hidden on the form.', required=False)), ('code_lookup_product', cms.utils.blocks.SnippetChooserNoEditBlock(data_dictionary.models.Product, help_text='An arbitrary Product from which a code lookup link should be created. This is almost always NOT what you want; please see and consider the other options first. If the product becomes deleted, this code lookup will will be automatically hidden on the form.', required=False)), ('code_lookup_vendor', cms.query_forms.blocks.VendorChooserBlock(help_text="An arbitrary Vendor from which a code lookup link should be created. This is almost always NOT what you want; please see and consider the other options first. Only vendors listed publicly on the Vendor List and having a Legacy vendor Code may be selected. If the Vendor becomes unavailable to show on the Vendor List or looses it's Legacy Vendor Code, then this code lookup will will be automatically hidden on the form.", required=False))], block_counts={'code_lookup_product': {'max_num': 100, 'min_num': 0}, 'code_lookup_table': {'max_num': 100, 'min_num': 0}, 'code_lookup_vendor': {'max_num': 100, 'min_num': 0}}, required=False)), ('allow_file_upload', wagtail.blocks.BooleanBlock(default=True, help_text='Check this to create an interface for file upload.', required=False)), ('file_upload_allowed_extensions', wagtail.blocks.RegexBlock(default='txt csv', help_text='File type extensions, files of which will be allowed for upload. Enter a space separated list, or blank to allow any file type.', regex='^\\w+( +\\w+)*$', required=False)), ('file_upload_label', wagtail.blocks.CharBlock(default='Company Codes Upload File', help_text='The label the field for screen readers and to be used when referring to the field in error messaging. This value will also be used as the placeholder text, visible in the browse for file entry box until the user has selected a file.', required=False)), ('file_upload_caption', wagtail.blocks.CharBlock(default='Upload a plain text file (.txt), having one code per line.', help_text='The caption to put under the file upload box.', required=False)), ('allow_search_entire_database', wagtail.blocks.BooleanBlock(default=True, help_text='Check this to allow users to search the entire database.', required=False)), ('search_entire_database_caption', wagtail.blocks.CharBlock(default='This method allows you to search the entire database of records. Please be aware that this method can take a very long time to run because it is dependent upon the size of the database.', help_text="The caption to put under the 'Search Entire Database' radio button.", required=False))], required=True))]))], label='Code Entry Step')), ('output_options_step', wagtail.blocks.StructBlock([('output_options_block', wagtail.blocks.StreamBlock([('descriptive_text', wagtail.blocks.CharBlock(default='Select the desired format of the output file. For large data requests, select a compression type to expedite downloads. If you enter your email address, you will receive an email that contains a URL to the output file when the data request is finished processing.', help_text='This is the text that will be shown above the output options.', required=True)), ('output_formats', wagtail.blocks.StructBlock([('fixed_width_text', wagtail.blocks.BooleanBlock(default=True, help_text='Fixed-width text (*.txt)', required=False)), ('comma_delimited_text', wagtail.blocks.BooleanBlock(default=True, help_text='Comma-delimited text (*.csv)', required=False)), ('excel_spreadsheet', wagtail.blocks.BooleanBlock(default=True, help_text='Excel spreadsheet (*.xlsx)', required=False)), ('tab_delimited_text', wagtail.blocks.BooleanBlock(default=True, help_text='Tab-delimited text (*.txt)', required=False)), ('html_table', wagtail.blocks.BooleanBlock(default=True, help_text='HTML table (*.htm)', required=False)), ('sas_windows_32_dataset', wagtail.blocks.BooleanBlock(default=True, help_text='SAS Windows_32 dataset (*.sas7bdat)', required=False)), ('sas_windows_64_dataset', wagtail.blocks.BooleanBlock(default=True, help_text='SAS Windows_64 dataset (*.sas7bdat)', required=False)), ('sas_solaris_64_dataset', wagtail.blocks.BooleanBlock(default=True, help_text='SAS Solaris_64 dataset (*.sas7bdat)', required=False)), ('dbase_file', wagtail.blocks.BooleanBlock(default=True, help_text='dBase file (*.dbf)', required=False)), ('stata_file', wagtail.blocks.BooleanBlock(default=True, help_text='STATA file (*.dta)', required=False)), ('spss_file', wagtail.blocks.BooleanBlock(default=True, help_text='SPSS file (*.sav)', required=False))], help_text='Allows the creator to choose which output formats will be available for users ofthis query page.')), ('output_compression', wagtail.blocks.StructBlock([('no_compression', wagtail.blocks.BooleanBlock(default=True, help_text='No compression', required=False)), ('zip', wagtail.blocks.BooleanBlock(default=True, help_text='zip (*.zip)', required=False)), ('gzip', wagtail.blocks.BooleanBlock(default=True, help_text='gzip (*.gz)', required=False))], help_text='Allows the creator to choose which output compression options will be available for users of this query page.')), ('date_formats', wagtail.blocks.StructBlock([('YYMMDDn8', wagtail.blocks.BooleanBlock(default=True, help_text='YYMMDDn8. (e.g. 19840725)', required=False)), ('DATE9', wagtail.blocks.BooleanBlock(default=True, help_text='DATE9. (e.g. 25JUL1984)', required=False)), ('DDMMYY6', wagtail.blocks.BooleanBlock(default=True, help_text='DDMMYY6. (e.g. 250784)', required=False)), ('MMDDYY10', wagtail.blocks.BooleanBlock(default=True, help_text='MMDDYY10. (e.g. 07/25/1984)', required=False)), ('DDMMYY10', wagtail.blocks.BooleanBlock(default=True, help_text='DDMMYY10. (e.g. 25/07/1984)', required=False)), ('YYMMDDs10', wagtail.blocks.BooleanBlock(default=True, help_text='YYMMDDs10. (e.g. 1984/07/25)', required=False))], help_text='Allows the creator to choose which date formats can be selected by users of this query page.'))], block_counts={'date_formats': {'max_num': 1, 'min_num': 0}, 'descriptive_text': {'max_num': 1, 'min_num': 1}, 'output_compression': {'max_num': 1, 'min_num': 0}, 'output_formats': {'max_num': 1, 'min_num': 0}}, max_num=4, min_num=2))], icon='placeholder', label='Output Options Step')), ('variable_picker', wagtail.blocks.StructBlock([('tabs', wagtail.blocks.StreamBlock([('tab', wagtail.blocks.StructBlock([('form_field_name', wagtail.blocks.CharBlock(default='var', help_text='Field name used as the key, in the payload, for the values entered by the user, when submitting the resulting Query to Query Manager.')), ('tab_source', wagtail.blocks.StreamBlock([('table_tab', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(help_text='Optional label of the Variable Picker tab, replaces replaces code/description of the Table from Data Dictionary', required=False)), ('table', cms.query_forms.blocks.TableChooserBlock(help_text='The Table from which this tab allows the selection of Variables. The Table choices are limited to those tables that exist within the Product selected on this Query Form and are not deleted. If the table becomes deleted this tab will be automatically hidden on the form.')), ('variable_inclusion', wagtail.blocks.ChoiceBlock(choices=[('inclusive', 'Inclusive: Show all Variables within the Table, except exclusions selected below'), ('selective', 'Selective: Show only the Variables with extended context below')], help_text='How variables are selected from tables.')), ('variable_order_first_sort', wagtail.blocks.ChoiceBlock(choices=[('form_position', 'Form Position: Order variables the same as they are ordered below'), ('dictionary_position', "Dictionary Position: Order variables according to their 'position' in the Data Dictionary"), ('code', 'Variable Code Position: Order variables lexicographically by their code')], help_text='How variables are ordered in the tab; this sort will be applied first and is required.')), ('variable_order_second_sort', wagtail.blocks.ChoiceBlock(choices=[('form_position', 'Form Position: Order variables the same as they are ordered below'), ('dictionary_position', "Dictionary Position: Order variables according to their 'position' in the Data Dictionary"), ('code', 'Variable Code Position: Order variables lexicographically by their code')], help_text='Optionally, a second sort to apply to variable ordering in the tab.', required=False)), ('variables', wagtail.blocks.StreamBlock([('extended_variable_context', wagtail.blocks.StructBlock([('variable', cms.query_forms.blocks.VariableChooserBlock(help_text='A Variable for which extended context is to be defined. Choices are limited to the Variables that exist within the Table selected for this Tab and are not deleted. If a variable becomes deleted, it will be automatically hidden on the tab.', label='Variable')), ('label', wagtail.blocks.CharBlock(help_text='Optional label, replaces code/description of the Variable from Data Dictionary', label='Label Override', required=False)), ('selection_state', wagtail.blocks.ChoiceBlock(choices=[('enabled', 'Enabled'), ('selected', 'Selected'), ('required', 'Required'), ('disabled', 'Disabled')], help_text='The state of the checkbox when the form page loads')), ('exclude_from_conditional', wagtail.blocks.BooleanBlock(default=False, help_text='If this variable is to be excluded from the conditional builder', required=False))])), ('excluded_variable', wagtail.blocks.StructBlock([('variable', cms.query_forms.blocks.VariableChooserBlock(help_text='This variable is to be excluded from the picker tab. Choices are limited to the Variables that exist within the Table selected for this Tab and are not deleted. The future deleted state of a variable will NOT influence variable exclusion rules.', label='Variable'))])), ('arbitrary_variable', wagtail.blocks.StructBlock([('variable_code', wagtail.blocks.CharBlock(help_text='The code for an arbitrary option in the Variable Picker', label='Variable Code')), ('label', wagtail.blocks.CharBlock(help_text='Optional Label to use in the picker, replaces the Variable Code above.', required=False)), ('selection_state', wagtail.blocks.ChoiceBlock(choices=[('enabled', 'Enabled'), ('selected', 'Selected'), ('required', 'Required'), ('disabled', 'Disabled')], help_text='The state of the checkbox when the form page loads')), ('variable_type', wagtail.blocks.ChoiceBlock(choices=[(1, 'Integer'), (2, 'Small Integer'), (3, 'Big Integer'), (4, 'Boolean'), (5, 'Decimal'), (6, 'Float'), (7, 'Date'), (8, 'Date Time'), (9, 'Time'), (10, 'Character Fixed'), (11, 'Character Varying'), (12, 'Text'), (13, 'UUID'), (14, 'Interval'), (15, 'JSON'), (16, 'Text Search Vector')], help_text='The type of this arbitrary option')), ('exclude_from_conditional', wagtail.blocks.BooleanBlock(default=False, help_text='If this variable is to be excluded from the conditional builder', required=False))]))], help_text='Optionally, choose variables for selective display, define additional context, and/or exclude variables. Additional variable context with duplicate/overlapping Variable chosen will produce unpredictable results. Variable exclusion supersedes all other settings.', required=False))])), ('arbitrary_tab', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock(help_text='Required label of the Variable Picker arbitrary tab.')), ('variables', wagtail.blocks.StreamBlock([('arbitrary_variable', wagtail.blocks.StructBlock([('variable_code', wagtail.blocks.CharBlock(help_text='The code for an arbitrary option in the Variable Picker', label='Variable Code')), ('label', wagtail.blocks.CharBlock(help_text='Optional Label to use in the picker, replaces the Variable Code above.', required=False)), ('selection_state', wagtail.blocks.ChoiceBlock(choices=[('enabled', 'Enabled'), ('selected', 'Selected'), ('required', 'Required'), ('disabled', 'Disabled')], help_text='The state of the checkbox when the form page loads')), ('variable_type', wagtail.blocks.ChoiceBlock(choices=[(1, 'Integer'), (2, 'Small Integer'), (3, 'Big Integer'), (4, 'Boolean'), (5, 'Decimal'), (6, 'Float'), (7, 'Date'), (8, 'Date Time'), (9, 'Time'), (10, 'Character Fixed'), (11, 'Character Varying'), (12, 'Text'), (13, 'UUID'), (14, 'Interval'), (15, 'JSON'), (16, 'Text Search Vector')], help_text='The type of this arbitrary option')), ('exclude_from_conditional', wagtail.blocks.BooleanBlock(default=False, help_text='If this variable is to be excluded from the conditional builder', required=False))]))], help_text='Define arbitrary variables for this tab.', min_num=1, required=True))]))], help_text='Tabs should be build from Data Dictionary tables whenever possible.', max_num=1, min_num=1, required=True))], help_text="Each tab must define it's own Form Field Name, but multiple tabs may use the same Form Field Name."))], block_counts={'tab': {'max_num': 99, 'min_num': 1}}, help_text='Web Forms allow the selection of Variables. "Variables" are the column names of WRDS\' financial data tables. A Variable Picker will allow you, the creator of this Web Form, to select one or more Tables (constrained to the tables within the selected Product - a.k.k. Schema), and then define which Variables from those Tables that the user may select for extraction into the output.
One or more tabs are presented in a Variable Picker. When two or more tabs are present, the "Search All" tab also appears.'))], label='Variable Picker')), ('conditional_query_builder', wagtail.blocks.StructBlock([('date_format', wagtail.blocks.ChoiceBlock(choices=[('YYYY-MM-DD', "ISO 8601, 'YYYY-MM-DD[ HH:mm:dd]'"), ('DDMMMYYYY', "SAS DATE9, 'ddMMMyyyy[:HH:mm:dd]'9")], help_text='Applies to Date and Datetime', label='What is the date format?'))], label='Conditional Query Builder')), ('recaptcha', wagtail.blocks.StructBlock([('label', wagtail.blocks.CharBlock()), ('help_text', wagtail.blocks.CharBlock(required=False))], icon='success', label='ReCAPTCHA field')), ('time', wagtail.blocks.StructBlock([('form_field_name', wagtail.blocks.CharBlock(help_text='Field name used as the key, in the payload, for the values entered by the user, when submitting the resulting Query to Query Manager.

Within time field, this key will be entered into the payload three times, one each suffixed by _hh, _mm, and _ss, and will include the hours, minutes, and seconds, respectively, of the time selected by the user.')), ('label', wagtail.blocks.CharBlock()), ('help_text', wagtail.blocks.CharBlock(required=False)), ('required', wagtail.blocks.BooleanBlock(required=False)), ('default_value', wagtail.blocks.TimeBlock(format='%H:%M:%S', required=False))], icon='time', label='Time field'))], use_json_field=True, verbose_name='Fields'), + ), + ] diff --git a/wagtailstreamforms/models/form.py b/wagtailstreamforms/models/form.py index 3eaf9a23..c6fde30f 100644 --- a/wagtailstreamforms/models/form.py +++ b/wagtailstreamforms/models/form.py @@ -8,11 +8,9 @@ MultiFieldPanel, ObjectList, PageChooserPanel, - StreamFieldPanel, TabbedInterface, ) from wagtail.models import Site - from wagtailstreamforms import hooks from wagtailstreamforms.conf import get_setting from wagtailstreamforms.fields import HookSelectField @@ -43,7 +41,7 @@ class AbstractForm(models.Model): template_name = models.CharField( _("Template"), max_length=255, choices=get_setting("FORM_TEMPLATES") ) - fields = FormFieldsStreamField([], verbose_name=_("Fields")) + fields = FormFieldsStreamField([], use_json_field=True, verbose_name=_("Fields")) submit_button_text = models.CharField( _("Submit button text"), max_length=100, default="Submit" ) @@ -90,7 +88,7 @@ class AbstractForm(models.Model): PageChooserPanel("post_redirect_page"), ] - field_panels = [StreamFieldPanel("fields")] + field_panels = [FieldPanel("fields")] edit_handler = TabbedInterface( [ From 0bdc091a632f53f88c2c4fb7096b4e5866a0f44f Mon Sep 17 00:00:00 2001 From: Ryan Sullivan Date: Mon, 14 Nov 2022 14:15:30 -0500 Subject: [PATCH 16/33] allow wagtail 4.1 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 430e6a94..370ab9ba 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ install_requires = [ - "wagtail>=3,<=4.1", + "wagtail>=3,<=4.2", "Unidecode>=0.04.14,<2.0", ] From e1214f2d2689a9eacbaedc09dc8a9871380b644d Mon Sep 17 00:00:00 2001 From: Ryan Sullivan Date: Tue, 19 Dec 2023 10:13:24 -0500 Subject: [PATCH 17/33] Adds get_local_blocks to allow for overriding the local_blocks to be added to the StructBlock for a field. --- wagtailstreamforms/fields.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/wagtailstreamforms/fields.py b/wagtailstreamforms/fields.py index 8b0a22bd..ccab103c 100644 --- a/wagtailstreamforms/fields.py +++ b/wagtailstreamforms/fields.py @@ -155,16 +155,28 @@ def get_form_block(self): :return: The ``wagtail.blocks.StructBlock`` to be used in the StreamField """ return blocks.StructBlock( - [ - ("label", blocks.CharBlock()), - ("help_text", blocks.CharBlock(required=False)), - ("required", blocks.BooleanBlock(required=False)), - ("default_value", blocks.CharBlock(required=False)), - ], + self.get_local_blocks(), icon=self.icon, label=self.label, ) + def get_local_blocks(self): + """The blocks that should be added to the StructBlock for this field. + + Override this to add blocks to, or remove blocks from, the StructBlock + before it is instantiated. This is useful because adding blocks to the + StructBlock after instantiation requires mucking with the StructBlock's + internal, undocumented API. + + :return: A list of tuples containing the block name and block instance. + """ + return [ + ("label", blocks.CharBlock()), + ("help_text", blocks.CharBlock(required=False)), + ("required", blocks.BooleanBlock(required=False)), + ("default_value", blocks.CharBlock(required=False)), + ] + class HookMultiSelectFormField(forms.MultipleChoiceField): widget = forms.CheckboxSelectMultiple From bfb9542598b1e1f01a1b9d54675d2dbe45065832 Mon Sep 17 00:00:00 2001 From: Ryan Sullivan Date: Fri, 5 Jan 2024 14:36:23 -0500 Subject: [PATCH 18/33] Adds yet another extension point, `get_form_block_class`, to allow the return of subclass of a structural block for further control over the block class, such as overriding the clean() method to provide custom validation. --- wagtailstreamforms/fields.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/wagtailstreamforms/fields.py b/wagtailstreamforms/fields.py index c8690a8f..7eae08ad 100644 --- a/wagtailstreamforms/fields.py +++ b/wagtailstreamforms/fields.py @@ -147,14 +147,27 @@ def get_options(self, block_value): "initial": self.get_formfield_initial(block_value), } + def get_form_block_class(self): + """ + The StreamField block class to be created for this field. This is + almost always a StructBlock, but conceptually it could be any structural block. + + Override this method and return a subclass of a structural block for further + control over the block class, such as overriding the clean() method to provide + custom validation. + :return: The ``wagtail.blocks.StructBlock`` to be used in the StreamField + """ + return blocks.StructBlock + def get_form_block(self): - """The StreamField StructBlock. + """The StreamField block class. Override this to provide additional fields in the StreamField. - :return: The ``wagtail.blocks.StructBlock`` to be used in the StreamField + :return: The resuld of calling get_form_block_class() is to be used in the + StreamField """ - return blocks.StructBlock( + return self.get_form_block_class()( self.get_local_blocks(), icon=self.icon, label=self.label, From f1327582a2f5e86159249aea70ae2dfba86b95b6 Mon Sep 17 00:00:00 2001 From: Ryan Sullivan Date: Wed, 31 Jan 2024 16:34:38 -0500 Subject: [PATCH 19/33] Adjusts wagtailstreamforms/wagtailstreamforms_fields.py to use new self.get_form_block_class() and self.get_local_blocks() methods --- .../wagtailstreamforms_fields.py | 116 ++++++++++-------- 1 file changed, 68 insertions(+), 48 deletions(-) diff --git a/wagtailstreamforms/wagtailstreamforms_fields.py b/wagtailstreamforms/wagtailstreamforms_fields.py index ba3aa7f0..94f640b1 100644 --- a/wagtailstreamforms/wagtailstreamforms_fields.py +++ b/wagtailstreamforms/wagtailstreamforms_fields.py @@ -3,7 +3,6 @@ from django import forms from django.utils.translation import gettext_lazy as _ from wagtail import blocks - from wagtailstreamforms.conf import get_setting from wagtailstreamforms.fields import BaseField, register @@ -67,15 +66,18 @@ def get_options(self, block_value): options.update({"choices": choices}) return options + def get_local_blocks(self): + return [ + ("label", blocks.CharBlock()), + ("help_text", blocks.CharBlock(required=False)), + ("required", blocks.BooleanBlock(required=False)), + ("empty_label", blocks.CharBlock(required=False)), + ("choices", blocks.ListBlock(blocks.CharBlock(label="Option"))), + ] + def get_form_block(self): - return blocks.StructBlock( - [ - ("label", blocks.CharBlock()), - ("help_text", blocks.CharBlock(required=False)), - ("required", blocks.BooleanBlock(required=False)), - ("empty_label", blocks.CharBlock(required=False)), - ("choices", blocks.ListBlock(blocks.CharBlock(label="Option"))), - ], + return self.get_form_block_class()( + self.get_local_blocks(), icon=self.icon, label=self.label, ) @@ -97,14 +99,17 @@ def get_options(self, block_value): options.update({"choices": choices}) return options + def get_local_blocks(self): + return [ + ("label", blocks.CharBlock()), + ("help_text", blocks.CharBlock(required=False)), + ("required", blocks.BooleanBlock(required=False)), + ("choices", blocks.ListBlock(blocks.CharBlock(label="Option"))), + ] + def get_form_block(self): - return blocks.StructBlock( - [ - ("label", blocks.CharBlock()), - ("help_text", blocks.CharBlock(required=False)), - ("required", blocks.BooleanBlock(required=False)), - ("choices", blocks.ListBlock(blocks.CharBlock(label="Option"))), - ], + return self.get_form_block_class()( + self.get_local_blocks(), icon=self.icon, label=self.label, ) @@ -127,14 +132,17 @@ def get_options(self, block_value): options.update({"choices": choices}) return options + def get_local_blocks(self): + return [ + ("label", blocks.CharBlock()), + ("help_text", blocks.CharBlock(required=False)), + ("required", blocks.BooleanBlock(required=False)), + ("choices", blocks.ListBlock(blocks.CharBlock(label="Option"))), + ] + def get_form_block(self): - return blocks.StructBlock( - [ - ("label", blocks.CharBlock()), - ("help_text", blocks.CharBlock(required=False)), - ("required", blocks.BooleanBlock(required=False)), - ("choices", blocks.ListBlock(blocks.CharBlock(label="Option"))), - ], + return self.get_form_block_class()( + self.get_local_blocks(), icon=self.icon, label=self.label, ) @@ -157,14 +165,17 @@ def get_options(self, block_value): options.update({"choices": choices}) return options + def get_local_blocks(self): + return [ + ("label", blocks.CharBlock()), + ("help_text", blocks.CharBlock(required=False)), + ("required", blocks.BooleanBlock(required=False)), + ("choices", blocks.ListBlock(blocks.CharBlock(label="Option"))), + ] + def get_form_block(self): - return blocks.StructBlock( - [ - ("label", blocks.CharBlock()), - ("help_text", blocks.CharBlock(required=False)), - ("required", blocks.BooleanBlock(required=False)), - ("choices", blocks.ListBlock(blocks.CharBlock(label="Option"))), - ], + return self.get_form_block_class()( + self.get_local_blocks(), icon=self.icon, label=self.label, ) @@ -175,13 +186,16 @@ class CheckboxField(BaseField): icon = "tick-inverse" label = _("Checkbox field") + def get_local_blocks(self): + return [ + ("label", blocks.CharBlock()), + ("help_text", blocks.CharBlock(required=False)), + ("required", blocks.BooleanBlock(required=False)), + ] + def get_form_block(self): - return blocks.StructBlock( - [ - ("label", blocks.CharBlock()), - ("help_text", blocks.CharBlock(required=False)), - ("required", blocks.BooleanBlock(required=False)), - ], + return self.get_form_block_class()( + self.get_local_blocks(), icon=self.icon, label=self.label, ) @@ -200,13 +214,16 @@ class SingleFileField(BaseField): icon = "doc-full-inverse" label = _("File field") + def get_local_blocks(self): + return [ + ("label", blocks.CharBlock()), + ("help_text", blocks.CharBlock(required=False)), + ("required", blocks.BooleanBlock(required=False)), + ] + def get_form_block(self): - return blocks.StructBlock( - [ - ("label", blocks.CharBlock()), - ("help_text", blocks.CharBlock(required=False)), - ("required", blocks.BooleanBlock(required=False)), - ], + return self.get_form_block_class()( + self.get_local_blocks(), icon=self.icon, label=self.label, ) @@ -236,13 +253,16 @@ class MultiFileField(BaseField): icon = "doc-full-inverse" label = _("Files field") + def get_local_blocks(self): + return [ + ("label", blocks.CharBlock()), + ("help_text", blocks.CharBlock(required=False)), + ("required", blocks.BooleanBlock(required=False)), + ] + def get_form_block(self): - return blocks.StructBlock( - [ - ("label", blocks.CharBlock()), - ("help_text", blocks.CharBlock(required=False)), - ("required", blocks.BooleanBlock(required=False)), - ], + return self.get_form_block_class()( + self.get_local_blocks(), icon=self.icon, label=self.label, ) From 891b666ca172af5037e9cf2043f6ce2727132142 Mon Sep 17 00:00:00 2001 From: Ryan Sullivan Date: Wed, 31 Jan 2024 17:02:38 -0500 Subject: [PATCH 20/33] Adds yet another override point: `get_form_block_kwargs`. --- example/wagtailstreamforms_fields.py | 50 ++++++++++++++----- wagtailstreamforms/fields.py | 16 ++++-- .../wagtailstreamforms_fields.py | 21 +++----- 3 files changed, 58 insertions(+), 29 deletions(-) diff --git a/example/wagtailstreamforms_fields.py b/example/wagtailstreamforms_fields.py index 73d5c5b5..641d113f 100644 --- a/example/wagtailstreamforms_fields.py +++ b/example/wagtailstreamforms_fields.py @@ -1,7 +1,6 @@ +from captcha.fields import ReCaptchaField from django import forms from django.contrib.auth.models import User - -from captcha.fields import ReCaptchaField from wagtail import blocks from wagtailstreamforms.fields import BaseField, register @@ -19,11 +18,26 @@ def get_options(self, block_value): }) return options + def get_form_block_class(self): + return blocks.StructBlock + + def get_local_blocks(self): + return [ + ("label", blocks.CharBlock()), + ("help_text", blocks.CharBlock(required=False)), + ] + + def get_form_block_kwargs(self): + return { + "icon": self.icon, + "label": self.label, + } + def get_form_block(self): - return blocks.StructBlock([ - ('label', blocks.CharBlock()), - ('help_text', blocks.CharBlock(required=False)), - ], icon=self.icon, label=self.label) + return self.get_form_block_class()( + self.get_local_blocks(), + **self.get_form_block_kwargs(), + ) @register('regex_validated') @@ -45,15 +59,21 @@ def get_regex_choices(self): ('^[a-zA-Z0-9]+$', 'Letters and numbers only'), ) - def get_form_block(self): - return blocks.StructBlock([ + def get_local_blocks(self): + return [ ('label', blocks.CharBlock()), ('help_text', blocks.CharBlock(required=False)), ('required', blocks.BooleanBlock(required=False)), ('regex', blocks.ChoiceBlock(choices=self.get_regex_choices())), ('error_message', blocks.CharBlock()), ('default_value', blocks.CharBlock(required=False)), - ], icon=self.icon, label=self.label) + ] + + def get_form_block(self): + return self.get_form_block_class()( + self.get_local_blocks(), + **self.get_form_block_kwargs() + ) @register('user') @@ -71,9 +91,15 @@ def get_options(self, block_value): options.update({'queryset': self.get_queryset()}) return options - def get_form_block(self): - return blocks.StructBlock([ + def get_local_blocks(self): + return [ ('label', blocks.CharBlock()), ('help_text', blocks.CharBlock(required=False)), ('required', blocks.BooleanBlock(required=False)), - ], icon=self.icon, label=self.label) + ] + + def get_form_block(self): + return self.get_form_block_class()( + self.get_local_blocks(), + **self.get_form_block_kwargs() + ) diff --git a/wagtailstreamforms/fields.py b/wagtailstreamforms/fields.py index 7eae08ad..5e8bcb43 100644 --- a/wagtailstreamforms/fields.py +++ b/wagtailstreamforms/fields.py @@ -25,7 +25,6 @@ class SingleLineTextField(BaseField): """ if cls is None: - def decorator(cls): register(field_name, cls) return cls @@ -159,6 +158,18 @@ def get_form_block_class(self): """ return blocks.StructBlock + def get_form_block_kwargs(self): + """The kwargs to be passed into the StreamField block class. + + Override this to provide additional kwargs to the StreamField block class. + + :return: The kwargs to be passed into the StreamField block class + """ + return { + "icon": self.icon, + "label": self.label, + } + def get_form_block(self): """The StreamField block class. @@ -169,8 +180,7 @@ def get_form_block(self): """ return self.get_form_block_class()( self.get_local_blocks(), - icon=self.icon, - label=self.label, + **self.get_form_block_kwargs(), ) def get_local_blocks(self): diff --git a/wagtailstreamforms/wagtailstreamforms_fields.py b/wagtailstreamforms/wagtailstreamforms_fields.py index 94f640b1..e3719ebb 100644 --- a/wagtailstreamforms/wagtailstreamforms_fields.py +++ b/wagtailstreamforms/wagtailstreamforms_fields.py @@ -78,8 +78,7 @@ def get_local_blocks(self): def get_form_block(self): return self.get_form_block_class()( self.get_local_blocks(), - icon=self.icon, - label=self.label, + **self.get_form_block_kwargs(), ) @@ -110,8 +109,7 @@ def get_local_blocks(self): def get_form_block(self): return self.get_form_block_class()( self.get_local_blocks(), - icon=self.icon, - label=self.label, + **self.get_form_block_kwargs(), ) @@ -143,8 +141,7 @@ def get_local_blocks(self): def get_form_block(self): return self.get_form_block_class()( self.get_local_blocks(), - icon=self.icon, - label=self.label, + **self.get_form_block_kwargs(), ) @@ -176,8 +173,7 @@ def get_local_blocks(self): def get_form_block(self): return self.get_form_block_class()( self.get_local_blocks(), - icon=self.icon, - label=self.label, + **self.get_form_block_kwargs(), ) @@ -196,8 +192,7 @@ def get_local_blocks(self): def get_form_block(self): return self.get_form_block_class()( self.get_local_blocks(), - icon=self.icon, - label=self.label, + **self.get_form_block_kwargs(), ) @@ -224,8 +219,7 @@ def get_local_blocks(self): def get_form_block(self): return self.get_form_block_class()( self.get_local_blocks(), - icon=self.icon, - label=self.label, + **self.get_form_block_kwargs(), ) @@ -263,8 +257,7 @@ def get_local_blocks(self): def get_form_block(self): return self.get_form_block_class()( self.get_local_blocks(), - icon=self.icon, - label=self.label, + **self.get_form_block_kwargs(), ) From 170a947e9d30ed6633b16e389bbecc58d6aba352 Mon Sep 17 00:00:00 2001 From: rense Date: Thu, 29 Feb 2024 20:29:16 +0100 Subject: [PATCH 21/33] Compatible Wagtail 6.0.1, Django 5.0.2 --- example/settings.py | 2 +- pyproject.toml | 2 +- readthedocs.yml | 2 +- requirements.txt | 2 +- setup.py | 15 +++++------- tests/blocks/test_form_block.py | 24 +++++++++---------- tests/settings.py | 2 +- tests/templatetags/test_form.py | 24 +++++++++---------- tests/urls.py | 2 +- tox.ini | 8 ++++--- wagtailstreamforms/conf.py | 2 +- wagtailstreamforms/migrations/0001_initial.py | 3 +-- .../migrations/0003_alter_form_fields.py | 3 ++- .../streamforms/advanced_settings.html | 2 +- .../templates/streamforms/confirm_copy.html | 2 +- .../streamforms/index_submissions.html | 2 +- wagtailstreamforms/views/copy.py | 7 ++---- wagtailstreamforms/views/submission_delete.py | 2 +- wagtailstreamforms/views/submission_list.py | 2 +- wagtailstreamforms/wagtail_hooks.py | 13 ++++------ 20 files changed, 56 insertions(+), 65 deletions(-) diff --git a/example/settings.py b/example/settings.py index 56241b04..dbe855e9 100644 --- a/example/settings.py +++ b/example/settings.py @@ -37,7 +37,7 @@ 'wagtail.search', 'wagtail.contrib.redirects', 'wagtail.sites', - 'wagtail.contrib.modeladmin', + 'wagtail_modeladmin', 'wagtail.contrib.postgres_search', 'wagtail.contrib.settings', 'wagtail.contrib.search_promotions', diff --git a/pyproject.toml b/pyproject.toml index c8c642a5..7d5e067d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.black] line-length=99 -target-version=["py39"] +target-version=["py310"] exclude = ''' /( \.git diff --git a/readthedocs.yml b/readthedocs.yml index 06897971..59a3922f 100644 --- a/readthedocs.yml +++ b/readthedocs.yml @@ -1,5 +1,5 @@ --- python: - version: 3.5 + version: 3.10 pip_install: true requirements_file: docs/requirements.txt diff --git a/requirements.txt b/requirements.txt index 1f7ad04c..58f87345 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ # The app itself -e . -Django>=3.2 +Django>=5 mock psycopg2-binary diff --git a/setup.py b/setup.py index 87ec759e..c6558487 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,7 @@ install_requires = [ - "wagtail>=4.1,<5.3", + "wagtail>5.2,<=6.0.1", "Unidecode>=0.04.14,<2.0", "wagtail-generic-chooser>=0.5.0,<0.7", ] @@ -61,17 +61,14 @@ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Framework :: Django", - "Framework :: Django :: 3.2", - "Framework :: Django :: 4", - "Framework :: Django :: 4.0", - "Framework :: Django :: 4.1", - "Framework :: Django :: 4.2", + "Framework :: Django :: 5.0", "Framework :: Wagtail", - "Framework :: Wagtail :: 4", + "Framework :: Wagtail :: 5", + "Framework :: Wagtail :: 6", "Topic :: Internet :: WWW/HTTP :: Site Management", ], ) diff --git a/tests/blocks/test_form_block.py b/tests/blocks/test_form_block.py index c1b72d7f..7e88cc84 100644 --- a/tests/blocks/test_form_block.py +++ b/tests/blocks/test_form_block.py @@ -26,48 +26,48 @@ def test_render(self): [ "

Basic Form

", '
', - '', + '', '' % self.form.pk, '', '
' '' - '' + '' '

Help

' "
", '
' '' - '" '

Help

' "
" '
' '' - '' + '' '

Help

' "
" '
' '' - '' + '' '

Help

' "
" '
' '' - '' + '' '

Help

' "
" '
' '' - '' + '' '

Help

' "
" '
' '' - '' + '' '

Help

' "
" '
' '' - '' '' '' '' @@ -103,17 +103,17 @@ def test_render(self): "
" '
' '' - '' + '' '

Help

' "
" '
' '' - '' + '' '

Help

' "
" '
' '' - '' + '' '

Help

' "
" '' diff --git a/tests/settings.py b/tests/settings.py index ca1423f5..ae61af2c 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -25,7 +25,7 @@ "wagtail.contrib.redirects", "wagtail.contrib.forms", "wagtail.sites", - "wagtail.contrib.modeladmin", + "wagtail_modeladmin", "wagtail.contrib.settings", "taggit", "wagtailstreamforms", diff --git a/tests/templatetags/test_form.py b/tests/templatetags/test_form.py index 97b3bde5..645da863 100644 --- a/tests/templatetags/test_form.py +++ b/tests/templatetags/test_form.py @@ -24,48 +24,48 @@ def test_render(self): [ "

Basic Form

", '', - '', + '', '' % self.form.pk, '', '
' '' - '' + '' '

Help

' "
", '
' '' - '" '

Help

' "
" '
' '' - '' + '' '

Help

' "
" '
' '' - '' + '' '

Help

' "
" '
' '' - '' + '' '

Help

' "
" '
' '' - '' + '' '

Help

' "
" '
' '' - '' + '' '

Help

' "
" '
' '' - '' '' '' '' @@ -101,17 +101,17 @@ def test_render(self): "
" '
' '' - '' + '' '

Help

' "
" '
' '' - '' + '' '

Help

' "
" '
' '' - '' + '' '

Help

' "
" '' diff --git a/tests/urls.py b/tests/urls.py index 10877f70..225f73f6 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -1,7 +1,7 @@ from django.contrib import admin from django.urls import include, re_path -from wagtail.admin import urls as wagtailadmin_urls from wagtail import urls as wagtail_urls +from wagtail.admin import urls as wagtailadmin_urls urlpatterns = [ re_path(r"^admin/", admin.site.urls), diff --git a/tox.ini b/tox.ini index 44273018..3930d294 100644 --- a/tox.ini +++ b/tox.ini @@ -1,10 +1,11 @@ [tox] envlist = flake8 - py{311,312}-dj{41}-wt{41,50,51,52} + py{310,311,312}-dj{50}-wt{50,51,52,60} [gh-actions] python = + "3.10": py310 "3.11": py311 "3.12": py312 @@ -12,16 +13,17 @@ python = deps = coverage mock - dj41: Django>=4.1,<4.2 - wt41: wagtail>=4.1,<4.2 + dj50: Django>=5.0,<5.1 wt50: wagtail>=5.0,<5.1 wt51: wagtail>=5.1,<5.2 wt52: wagtail>=5.2,<5.3 + wt60: wagtail>=6.0,<6.1 commands = coverage run manage.py test basepython = + py310: python3.10 py311: python3.11 py312: python3.12 diff --git a/wagtailstreamforms/conf.py b/wagtailstreamforms/conf.py index 245bd5b5..551dd212 100644 --- a/wagtailstreamforms/conf.py +++ b/wagtailstreamforms/conf.py @@ -24,7 +24,7 @@ "singlefile", "multifile", ), - "FORM_TEMPLATES": (("streamforms/form_block.html", "Default Form Template"),), + "FORM_TEMPLATES": [("streamforms/form_block.html", "Default Form Template")], } diff --git a/wagtailstreamforms/migrations/0001_initial.py b/wagtailstreamforms/migrations/0001_initial.py index 01eba326..90151c1e 100644 --- a/wagtailstreamforms/migrations/0001_initial.py +++ b/wagtailstreamforms/migrations/0001_initial.py @@ -1,9 +1,8 @@ # Generated by Django 2.0.5 on 2018-05-30 23:03 import django.db.models.deletion -from wagtail import blocks - from django.db import migrations, models +from wagtail import blocks import wagtailstreamforms.conf import wagtailstreamforms.fields diff --git a/wagtailstreamforms/migrations/0003_alter_form_fields.py b/wagtailstreamforms/migrations/0003_alter_form_fields.py index cb9e2318..a486033c 100644 --- a/wagtailstreamforms/migrations/0003_alter_form_fields.py +++ b/wagtailstreamforms/migrations/0003_alter_form_fields.py @@ -1,7 +1,8 @@ # Generated by Django 4.1.8 on 2023-04-14 05:21 -from django.db import migrations import wagtail.blocks +from django.db import migrations + import wagtailstreamforms.streamfield diff --git a/wagtailstreamforms/templates/streamforms/advanced_settings.html b/wagtailstreamforms/templates/streamforms/advanced_settings.html index a90254c5..e3f81f28 100644 --- a/wagtailstreamforms/templates/streamforms/advanced_settings.html +++ b/wagtailstreamforms/templates/streamforms/advanced_settings.html @@ -13,7 +13,7 @@
    {% block visible_fields %} {% for field in form.visible_fields %} - {% include "wagtailadmin/shared/field_as_li.html" %} + {% include "wagtailadmin/shared/field.html" %} {% endfor %} {% endblock %}
  • diff --git a/wagtailstreamforms/templates/streamforms/confirm_copy.html b/wagtailstreamforms/templates/streamforms/confirm_copy.html index 4d9a7ce6..dca0d89c 100644 --- a/wagtailstreamforms/templates/streamforms/confirm_copy.html +++ b/wagtailstreamforms/templates/streamforms/confirm_copy.html @@ -14,7 +14,7 @@
      {% block visible_fields %} {% for field in form.visible_fields %} - {% include "wagtailadmin/shared/field_as_li.html" %} + {% include "wagtailadmin/shared/field.html" %} {% endfor %} {% endblock %}
    • diff --git a/wagtailstreamforms/templates/streamforms/index_submissions.html b/wagtailstreamforms/templates/streamforms/index_submissions.html index 8a6f50da..054706f6 100644 --- a/wagtailstreamforms/templates/streamforms/index_submissions.html +++ b/wagtailstreamforms/templates/streamforms/index_submissions.html @@ -77,7 +77,7 @@