wagtailstreamforms 0.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wagtailstreamforms/__init__.py +1 -0
- wagtailstreamforms/blocks.py +93 -0
- wagtailstreamforms/conf.py +35 -0
- wagtailstreamforms/fields.py +218 -0
- wagtailstreamforms/forms.py +61 -0
- wagtailstreamforms/hooks.py +62 -0
- wagtailstreamforms/locale/es/LC_MESSAGES/django.mo +0 -0
- wagtailstreamforms/locale/es/LC_MESSAGES/django.po +328 -0
- wagtailstreamforms/locale/nl/LC_MESSAGES/django.mo +0 -0
- wagtailstreamforms/locale/nl/LC_MESSAGES/django.po +464 -0
- wagtailstreamforms/locale/tr/LC_MESSAGES/django.mo +0 -0
- wagtailstreamforms/locale/tr/LC_MESSAGES/django.po +317 -0
- wagtailstreamforms/management/__init__.py +0 -0
- wagtailstreamforms/management/commands/__init__.py +0 -0
- wagtailstreamforms/management/commands/prunesubmissions.py +29 -0
- wagtailstreamforms/migrations/0001_initial.py +555 -0
- wagtailstreamforms/migrations/0002_form_site.py +21 -0
- wagtailstreamforms/migrations/0003_alter_form_fields.py +20 -0
- wagtailstreamforms/migrations/__init__.py +0 -0
- wagtailstreamforms/models/__init__.py +39 -0
- wagtailstreamforms/models/abstract.py +15 -0
- wagtailstreamforms/models/file.py +35 -0
- wagtailstreamforms/models/form.py +188 -0
- wagtailstreamforms/models/submission.py +27 -0
- wagtailstreamforms/serializers.py +11 -0
- wagtailstreamforms/streamfield.py +48 -0
- wagtailstreamforms/templates/streamforms/advanced_settings.html +25 -0
- wagtailstreamforms/templates/streamforms/confirm_copy.html +26 -0
- wagtailstreamforms/templates/streamforms/confirm_delete.html +23 -0
- wagtailstreamforms/templates/streamforms/form_block.html +10 -0
- wagtailstreamforms/templates/streamforms/index_submissions.html +104 -0
- wagtailstreamforms/templates/streamforms/list_submissions.html +56 -0
- wagtailstreamforms/templates/streamforms/non_existent_form.html +2 -0
- wagtailstreamforms/templates/streamforms/partials/form_field.html +6 -0
- wagtailstreamforms/templates/streamforms/partials/pagination_nav.html +17 -0
- wagtailstreamforms/templates/streamforms/wagtailadmin/shared/datetimepicker_translations.html +39 -0
- wagtailstreamforms/templatetags/__init__.py +0 -0
- wagtailstreamforms/templatetags/streamforms_tags.py +43 -0
- wagtailstreamforms/urls.py +31 -0
- wagtailstreamforms/utils/__init__.py +0 -0
- wagtailstreamforms/utils/apps.py +25 -0
- wagtailstreamforms/utils/general.py +6 -0
- wagtailstreamforms/utils/loading.py +30 -0
- wagtailstreamforms/utils/requests.py +13 -0
- wagtailstreamforms/utils/version.py +0 -0
- wagtailstreamforms/views/__init__.py +4 -0
- wagtailstreamforms/views/advanced_settings.py +51 -0
- wagtailstreamforms/views/copy.py +83 -0
- wagtailstreamforms/views/submission_delete.py +73 -0
- wagtailstreamforms/views/submission_list.py +112 -0
- wagtailstreamforms/wagtail_hooks.py +255 -0
- wagtailstreamforms/wagtailstreamforms_fields.py +274 -0
- wagtailstreamforms/wagtailstreamforms_hooks.py +31 -0
- wagtailstreamforms-0.0.0.dist-info/METADATA +24 -0
- wagtailstreamforms-0.0.0.dist-info/RECORD +58 -0
- wagtailstreamforms-0.0.0.dist-info/WHEEL +5 -0
- wagtailstreamforms-0.0.0.dist-info/licenses/LICENSE +19 -0
- wagtailstreamforms-0.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "4.1.0"
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
|
|
3
|
+
from django.utils.functional import cached_property
|
|
4
|
+
from django.utils.safestring import SafeText, mark_safe
|
|
5
|
+
from django.utils.translation import gettext_lazy as _
|
|
6
|
+
from wagtail import blocks
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class InfoBlock(blocks.CharBlock):
|
|
10
|
+
def render_form(self, value, prefix="", errors=None) -> SafeText:
|
|
11
|
+
field = self.field
|
|
12
|
+
shown_value = value if value else field.help_text
|
|
13
|
+
return mark_safe('<div style="margin-top:5px;padding:0.9em 1.2em;">%s</div>' % shown_value)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class FormChooserBlock(blocks.ChooserBlock):
|
|
17
|
+
@cached_property
|
|
18
|
+
def target_model(self):
|
|
19
|
+
from .models import Form
|
|
20
|
+
|
|
21
|
+
return Form
|
|
22
|
+
|
|
23
|
+
@cached_property
|
|
24
|
+
def widget(self):
|
|
25
|
+
from .wagtail_hooks import WagtailStreamFormsChooser
|
|
26
|
+
|
|
27
|
+
return WagtailStreamFormsChooser()
|
|
28
|
+
|
|
29
|
+
def get_form_state(self, value: dict):
|
|
30
|
+
return self.widget.get_value_data(value)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class WagtailFormBlock(blocks.StructBlock):
|
|
34
|
+
form = FormChooserBlock()
|
|
35
|
+
form_action = blocks.CharBlock(
|
|
36
|
+
required=False,
|
|
37
|
+
help_text=_('The form post action. "" or "." for the current page or a url'),
|
|
38
|
+
)
|
|
39
|
+
form_reference = InfoBlock(
|
|
40
|
+
required=False,
|
|
41
|
+
help_text=_("This form will be given a unique reference once saved"),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
class Meta:
|
|
45
|
+
icon = "form"
|
|
46
|
+
template = None
|
|
47
|
+
|
|
48
|
+
def render(self, value: dict, context: dict | None = None) -> str | SafeText:
|
|
49
|
+
form = value.get("form")
|
|
50
|
+
|
|
51
|
+
# check if we have a form, as they can be deleted, and we dont want to break the site with
|
|
52
|
+
# a none template value
|
|
53
|
+
if form:
|
|
54
|
+
self.meta.template = form.template_name
|
|
55
|
+
else:
|
|
56
|
+
self.meta.template = "streamforms/non_existent_form.html"
|
|
57
|
+
|
|
58
|
+
return super().render(value, context)
|
|
59
|
+
|
|
60
|
+
def get_context(self, value: dict, parent_context: dict | None = None) -> dict:
|
|
61
|
+
context = super().get_context(value, parent_context)
|
|
62
|
+
|
|
63
|
+
form = value.get("form")
|
|
64
|
+
form_reference = value.get("form_reference")
|
|
65
|
+
|
|
66
|
+
if form:
|
|
67
|
+
# check the context for an invalid form submitted to the page.
|
|
68
|
+
# Use that instead if it has the same unique form_reference number
|
|
69
|
+
invalid_form_reference = context.get("invalid_stream_form_reference")
|
|
70
|
+
invalid_form = context.get("invalid_stream_form")
|
|
71
|
+
|
|
72
|
+
if (
|
|
73
|
+
invalid_form_reference
|
|
74
|
+
and invalid_form
|
|
75
|
+
and invalid_form_reference == form_reference
|
|
76
|
+
):
|
|
77
|
+
context["form"] = invalid_form
|
|
78
|
+
else:
|
|
79
|
+
context["form"] = form.get_form(
|
|
80
|
+
initial={"form_id": form.id, "form_reference": form_reference}
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
return context
|
|
84
|
+
|
|
85
|
+
def clean(self, value: dict) -> dict:
|
|
86
|
+
result = super().clean(value)
|
|
87
|
+
|
|
88
|
+
# set to a new uuid so we can ensure we can identify this form
|
|
89
|
+
# against other forms of the same type in the page
|
|
90
|
+
if not result.get("form_reference"):
|
|
91
|
+
result["form_reference"] = uuid.uuid4()
|
|
92
|
+
|
|
93
|
+
return result
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from django.conf import settings
|
|
2
|
+
from django.utils.translation import gettext_lazy as _
|
|
3
|
+
|
|
4
|
+
SETTINGS_PREFIX = "WAGTAILSTREAMFORMS"
|
|
5
|
+
SETTINGS_DEFAULTS = {
|
|
6
|
+
"ADMIN_MENU_LABEL": _("Streamforms"),
|
|
7
|
+
"ADMIN_MENU_ORDER": None,
|
|
8
|
+
"ADVANCED_SETTINGS_MODEL": None,
|
|
9
|
+
"ENABLE_FORM_PROCESSING": True,
|
|
10
|
+
"ENABLE_BUILTIN_HOOKS": True,
|
|
11
|
+
"ENABLED_FIELDS": (
|
|
12
|
+
"singleline",
|
|
13
|
+
"multiline",
|
|
14
|
+
"date",
|
|
15
|
+
"datetime",
|
|
16
|
+
"email",
|
|
17
|
+
"url",
|
|
18
|
+
"number",
|
|
19
|
+
"dropdown",
|
|
20
|
+
"radio",
|
|
21
|
+
"checkboxes",
|
|
22
|
+
"checkbox",
|
|
23
|
+
"hidden",
|
|
24
|
+
"singlefile",
|
|
25
|
+
"multifile",
|
|
26
|
+
),
|
|
27
|
+
"FORM_TEMPLATES": [
|
|
28
|
+
("streamforms/form_block.html", "Default Form Template"),
|
|
29
|
+
],
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_setting(name):
|
|
34
|
+
setting_key = "{}_{}".format(SETTINGS_PREFIX, name)
|
|
35
|
+
return getattr(settings, setting_key, SETTINGS_DEFAULTS[name])
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
from django import forms
|
|
2
|
+
from django.core import exceptions
|
|
3
|
+
from django.db import models
|
|
4
|
+
from django.utils.text import capfirst
|
|
5
|
+
from wagtail import blocks
|
|
6
|
+
|
|
7
|
+
from wagtailstreamforms import hooks
|
|
8
|
+
from wagtailstreamforms.utils.apps import get_app_submodules
|
|
9
|
+
from wagtailstreamforms.utils.general import get_slug_from_string
|
|
10
|
+
|
|
11
|
+
_fields = {}
|
|
12
|
+
_searched_for_fields = False
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def register(field_name, cls=None):
|
|
16
|
+
"""
|
|
17
|
+
Register field for ``field_name``. Can be used as a decorator::
|
|
18
|
+
@register('singleline')
|
|
19
|
+
class SingleLineTextField(BaseField):
|
|
20
|
+
field_class = django.forms.CharField
|
|
21
|
+
or as a function call::
|
|
22
|
+
class SingleLineTextField(BaseField):
|
|
23
|
+
field_class = django.forms.CharField
|
|
24
|
+
register('singleline', SingleLineTextField)
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
if cls is None:
|
|
28
|
+
|
|
29
|
+
def decorator(cls):
|
|
30
|
+
register(field_name, cls)
|
|
31
|
+
return cls
|
|
32
|
+
|
|
33
|
+
return decorator
|
|
34
|
+
|
|
35
|
+
_fields[field_name] = cls
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def search_for_fields():
|
|
39
|
+
global _searched_for_fields
|
|
40
|
+
if not _searched_for_fields:
|
|
41
|
+
list(get_app_submodules("wagtailstreamforms_fields"))
|
|
42
|
+
_searched_for_fields = True
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def get_fields():
|
|
46
|
+
"""Return the registered field classes."""
|
|
47
|
+
|
|
48
|
+
search_for_fields()
|
|
49
|
+
return _fields
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class BaseField:
|
|
53
|
+
"""A base form field class, all form fields must inherit this class.
|
|
54
|
+
|
|
55
|
+
Usage::
|
|
56
|
+
|
|
57
|
+
@register('multiline')
|
|
58
|
+
class MultiLineTextField(BaseField):
|
|
59
|
+
field_class = forms.CharField
|
|
60
|
+
widget = forms.widgets.Textarea
|
|
61
|
+
icon = 'placeholder'
|
|
62
|
+
label = 'Text (multi line)'
|
|
63
|
+
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
field_class = None
|
|
67
|
+
widget = None
|
|
68
|
+
icon = "placeholder"
|
|
69
|
+
label = None
|
|
70
|
+
|
|
71
|
+
@classmethod
|
|
72
|
+
def get_formfield_name(cls, block_value):
|
|
73
|
+
"""
|
|
74
|
+
Return a value to use as the 'name' for the Django form field.
|
|
75
|
+
"""
|
|
76
|
+
return block_value.get("name") or get_slug_from_string(
|
|
77
|
+
cls.get_formfield_label(block_value)
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
@classmethod
|
|
81
|
+
def get_formfield_label(cls, block_value):
|
|
82
|
+
"""
|
|
83
|
+
Return a value to use as the 'label' for the Django form field.
|
|
84
|
+
"""
|
|
85
|
+
try:
|
|
86
|
+
return block_value["label"]
|
|
87
|
+
except KeyError:
|
|
88
|
+
raise AttributeError(
|
|
89
|
+
f"No label value can be determined for an instance of {cls.__name__}. "
|
|
90
|
+
"Add a 'label' CharBlock() in your field's get_form_block() method to "
|
|
91
|
+
"allow this to be specified by form editors. Or, override "
|
|
92
|
+
"get_formfield_label() to return a different value."
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
@classmethod
|
|
96
|
+
def get_formfield_help_text(cls, block_value):
|
|
97
|
+
"""
|
|
98
|
+
Return a value to use as the 'help_text' for the Django form field.
|
|
99
|
+
"""
|
|
100
|
+
return block_value.get("help_text")
|
|
101
|
+
|
|
102
|
+
@classmethod
|
|
103
|
+
def get_formfield_initial(cls, block_value):
|
|
104
|
+
"""
|
|
105
|
+
Return a 'initial' value to use for the Django form field.
|
|
106
|
+
"""
|
|
107
|
+
return block_value.get("default_value")
|
|
108
|
+
|
|
109
|
+
@classmethod
|
|
110
|
+
def get_formfield_required(cls, block_value):
|
|
111
|
+
"""
|
|
112
|
+
Return a boolean indicating whether the Django form field should be required.
|
|
113
|
+
"""
|
|
114
|
+
return block_value.get("required", True)
|
|
115
|
+
|
|
116
|
+
def get_formfield(self, block_value):
|
|
117
|
+
"""
|
|
118
|
+
Get the form field. Its unlikely you will need to override this.
|
|
119
|
+
|
|
120
|
+
:param block_value: The StreamValue for this field from the StreamField
|
|
121
|
+
:return: An instance of a form field class, ie ``django.forms.CharField(**options)``
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
if not self.field_class:
|
|
125
|
+
raise NotImplementedError("must provide a cls.field_class")
|
|
126
|
+
|
|
127
|
+
options = self.get_options(block_value)
|
|
128
|
+
|
|
129
|
+
if self.widget:
|
|
130
|
+
return self.field_class(widget=self.widget, **options)
|
|
131
|
+
|
|
132
|
+
return self.field_class(**options)
|
|
133
|
+
|
|
134
|
+
def get_options(self, block_value):
|
|
135
|
+
"""The field options.
|
|
136
|
+
|
|
137
|
+
Override this to provide additional options such as ``choices`` for a dropdown.
|
|
138
|
+
|
|
139
|
+
:param block_value: The StreamValue for this field from the StreamField
|
|
140
|
+
:return: The options to be passed into the field, ie ``django.forms.CharField(**options)``
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
"label": self.get_formfield_label(block_value),
|
|
145
|
+
"help_text": self.get_formfield_help_text(block_value),
|
|
146
|
+
"required": self.get_formfield_required(block_value),
|
|
147
|
+
"initial": self.get_formfield_initial(block_value),
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
def get_form_block(self):
|
|
151
|
+
"""The StreamField StructBlock.
|
|
152
|
+
|
|
153
|
+
Override this to provide additional fields in the StreamField.
|
|
154
|
+
|
|
155
|
+
:return: The ``wagtail.core.blocks.StructBlock`` to be used in the StreamField
|
|
156
|
+
"""
|
|
157
|
+
return blocks.StructBlock(
|
|
158
|
+
[
|
|
159
|
+
("label", blocks.CharBlock()),
|
|
160
|
+
("help_text", blocks.CharBlock(required=False)),
|
|
161
|
+
("required", blocks.BooleanBlock(required=False)),
|
|
162
|
+
("default_value", blocks.CharBlock(required=False)),
|
|
163
|
+
],
|
|
164
|
+
icon=self.icon,
|
|
165
|
+
label=self.label,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class HookMultiSelectFormField(forms.MultipleChoiceField):
|
|
170
|
+
widget = forms.CheckboxSelectMultiple
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class HookSelectField(models.Field):
|
|
174
|
+
def get_choices_default(self):
|
|
175
|
+
return [
|
|
176
|
+
(fn.__name__, capfirst(fn.__name__.replace("_", " ")))
|
|
177
|
+
for fn in hooks.get_hooks("process_form_submission")
|
|
178
|
+
]
|
|
179
|
+
|
|
180
|
+
def get_db_prep_value(self, value, connection=None, prepared=False):
|
|
181
|
+
if isinstance(value, str):
|
|
182
|
+
return value
|
|
183
|
+
elif isinstance(value, list):
|
|
184
|
+
return ",".join(value)
|
|
185
|
+
|
|
186
|
+
def get_internal_type(self):
|
|
187
|
+
return "TextField"
|
|
188
|
+
|
|
189
|
+
def formfield(self, **kwargs):
|
|
190
|
+
defaults = {
|
|
191
|
+
"form_class": HookMultiSelectFormField,
|
|
192
|
+
"choices": self.get_choices_default(),
|
|
193
|
+
}
|
|
194
|
+
defaults.update(kwargs)
|
|
195
|
+
return super().formfield(**defaults)
|
|
196
|
+
|
|
197
|
+
def from_db_value(self, value, expression, connection, context=None, *args, **kwargs):
|
|
198
|
+
if value is None or value == "":
|
|
199
|
+
return []
|
|
200
|
+
return value.split(",")
|
|
201
|
+
|
|
202
|
+
def to_python(self, value):
|
|
203
|
+
if not value or value == "":
|
|
204
|
+
return []
|
|
205
|
+
if isinstance(value, list):
|
|
206
|
+
return value
|
|
207
|
+
return value.split(",")
|
|
208
|
+
|
|
209
|
+
def value_to_string(self, obj):
|
|
210
|
+
value = self.value_from_object(obj)
|
|
211
|
+
return ",".join(value)
|
|
212
|
+
|
|
213
|
+
def validate(self, value, model_instance):
|
|
214
|
+
arr_choices = [v for v, s in self.get_choices_default()]
|
|
215
|
+
for opt in value:
|
|
216
|
+
if opt not in arr_choices:
|
|
217
|
+
raise exceptions.ValidationError("%s is not a valid choice" % opt)
|
|
218
|
+
return
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from collections import OrderedDict
|
|
2
|
+
|
|
3
|
+
from django import forms
|
|
4
|
+
from django.utils.translation import gettext_lazy as _
|
|
5
|
+
|
|
6
|
+
from wagtailstreamforms.fields import get_fields
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BaseForm(forms.Form):
|
|
10
|
+
def __init__(self, *args, **kwargs):
|
|
11
|
+
kwargs.setdefault("label_suffix", "")
|
|
12
|
+
|
|
13
|
+
self.user = kwargs.pop("user", None)
|
|
14
|
+
self.page = kwargs.pop("page", None)
|
|
15
|
+
|
|
16
|
+
super().__init__(*args, **kwargs)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class FormBuilder:
|
|
20
|
+
def __init__(self, fields):
|
|
21
|
+
self.fields = fields
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def formfields(self):
|
|
25
|
+
"""Return a list of form fields from the registered fields."""
|
|
26
|
+
|
|
27
|
+
formfields = OrderedDict()
|
|
28
|
+
|
|
29
|
+
registered_fields = get_fields()
|
|
30
|
+
|
|
31
|
+
for field in self.fields:
|
|
32
|
+
field_type = field.get("type")
|
|
33
|
+
field_value = field.get("value")
|
|
34
|
+
|
|
35
|
+
# check we have the field
|
|
36
|
+
if field_type not in registered_fields:
|
|
37
|
+
raise AttributeError("Could not find a registered field of type %s" % field_type)
|
|
38
|
+
|
|
39
|
+
# get the field
|
|
40
|
+
registered_cls = registered_fields[field_type]()
|
|
41
|
+
field_name = registered_cls.get_formfield_name(field_value)
|
|
42
|
+
field_cls = registered_cls.get_formfield(field_value)
|
|
43
|
+
formfields[field_name] = field_cls
|
|
44
|
+
|
|
45
|
+
# add fields to uniquely identify the form
|
|
46
|
+
formfields["form_id"] = forms.CharField(widget=forms.HiddenInput)
|
|
47
|
+
formfields["form_reference"] = forms.CharField(widget=forms.HiddenInput)
|
|
48
|
+
|
|
49
|
+
return formfields
|
|
50
|
+
|
|
51
|
+
def get_form_class(self):
|
|
52
|
+
return type(str("StreamformsForm"), (BaseForm,), self.formfields)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class SelectDateForm(forms.Form):
|
|
56
|
+
date_from = forms.DateTimeField(
|
|
57
|
+
required=False, widget=forms.DateInput(attrs={"placeholder": _("Date from")})
|
|
58
|
+
)
|
|
59
|
+
date_to = forms.DateTimeField(
|
|
60
|
+
required=False, widget=forms.DateInput(attrs={"placeholder": _("Date to")})
|
|
61
|
+
)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from operator import itemgetter
|
|
2
|
+
|
|
3
|
+
from wagtailstreamforms.conf import get_setting
|
|
4
|
+
from wagtailstreamforms.utils.apps import get_app_submodules
|
|
5
|
+
|
|
6
|
+
_hooks = {}
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def register(hook_name, fn=None, order=0):
|
|
10
|
+
"""
|
|
11
|
+
Register hook for ``hook_name``. Can be used as a decorator::
|
|
12
|
+
@register('hook_name')
|
|
13
|
+
def my_hook(...):
|
|
14
|
+
pass
|
|
15
|
+
or as a function call::
|
|
16
|
+
def my_hook(...):
|
|
17
|
+
pass
|
|
18
|
+
register('hook_name', my_hook)
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
# Pretend to be a decorator if fn is not supplied
|
|
22
|
+
if fn is None:
|
|
23
|
+
|
|
24
|
+
def decorator(fn):
|
|
25
|
+
register(hook_name, fn, order=order)
|
|
26
|
+
return fn
|
|
27
|
+
|
|
28
|
+
return decorator
|
|
29
|
+
|
|
30
|
+
if hook_name not in _hooks:
|
|
31
|
+
_hooks[hook_name] = []
|
|
32
|
+
_hooks[hook_name].append((fn, order))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
_searched_for_hooks = False
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def search_for_hooks():
|
|
39
|
+
global _searched_for_hooks
|
|
40
|
+
if not _searched_for_hooks:
|
|
41
|
+
list(get_app_submodules("wagtailstreamforms_hooks"))
|
|
42
|
+
_searched_for_hooks = True
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def get_hooks(hook_name):
|
|
46
|
+
"""Return the hooks function sorted by their order."""
|
|
47
|
+
|
|
48
|
+
search_for_hooks()
|
|
49
|
+
hooks = _hooks.get(hook_name, [])
|
|
50
|
+
hooks = sorted(hooks, key=itemgetter(1))
|
|
51
|
+
fncs = []
|
|
52
|
+
builtin_hook_modules = ["wagtailstreamforms.wagtailstreamforms_hooks"]
|
|
53
|
+
builtin_enabled = get_setting("ENABLE_BUILTIN_HOOKS")
|
|
54
|
+
|
|
55
|
+
for fn, _ in hooks:
|
|
56
|
+
# dont add the hooks if they have been disabled via the setting
|
|
57
|
+
# this is so that they can be overridden
|
|
58
|
+
if fn.__module__ in builtin_hook_modules and not builtin_enabled:
|
|
59
|
+
continue
|
|
60
|
+
fncs.append(fn)
|
|
61
|
+
|
|
62
|
+
return fncs
|
|
Binary file
|