Allow plugins to disable and pre-fill questions and contact form fields (#1824)

Co-authored-by: Raphael Michel <michel@rami.io>
Co-authored-by: Raphael Michel <mail@raphaelmichel.de>
This commit is contained in:
Martin Gross
2020-10-26 09:30:16 +01:00
committed by GitHub
parent c2069663f3
commit 4fed690209
5 changed files with 143 additions and 6 deletions

View File

@@ -14,6 +14,7 @@ from pretix.base.models import (
CartPosition, InvoiceAddress, OrderPosition, Question, QuestionAnswer,
QuestionOption,
)
from pretix.presale.signals import contact_form_fields_overrides
class BaseQuestionsViewMixin:
@@ -34,6 +35,9 @@ class BaseQuestionsViewMixin:
def _positions_for_questions(self):
raise NotImplementedError()
def get_question_override_sets(self, position):
return []
@cached_property
def forms(self):
"""
@@ -62,6 +66,23 @@ class BaseQuestionsViewMixin:
self.request.event.settings.attendee_addresses_asked
))
)
override_sets = self.get_question_override_sets(cr)
for overrides in override_sets:
for question_name, question_field in form.fields.items():
if hasattr(question_field, 'question'):
if question_field.question.identifier in overrides:
if 'initial' in overrides[question_field.question.identifier]:
question_field.initial = overrides[question_field.question.identifier]['initial']
if 'disabled' in overrides[question_field.question.identifier]:
question_field.disabled = overrides[question_field.question.identifier]['disabled']
else:
if question_name in overrides:
if 'initial' in overrides[question_name]:
question_field.initial = overrides[question_name]['initial']
if 'disabled' in overrides[question_name]:
question_field.disabled = overrides[question_name]['disabled']
if len(form.fields) > 0:
formlist.append(form)
return formlist
@@ -213,24 +234,47 @@ class OrderQuestionsViewMixin(BaseQuestionsViewMixin):
self.order.total != Decimal('0.00') or not self.request.event.settings.invoice_address_not_asked_free
)
@cached_property
def _contact_override_sets(self):
override_sets = [
resp for recv, resp in contact_form_fields_overrides.send(
self.request.event,
request=self.request,
order=self.order,
)
]
for override in override_sets:
for k in override:
# We don't want initial values to be modified, they should come from the order directly
override[k].pop('initial', None)
return override_sets
@cached_property
def invoice_form(self):
if not self.address_asked and self.request.event.settings.invoice_name_required:
return self.invoice_name_form_class(
f = self.invoice_name_form_class(
data=self.request.POST if self.request.method == "POST" else None,
event=self.request.event,
instance=self.invoice_address, validate_vat_id=False,
all_optional=self.all_optional
)
if self.address_asked:
return self.invoice_form_class(
elif self.address_asked:
f = self.invoice_form_class(
data=self.request.POST if self.request.method == "POST" else None,
event=self.request.event,
instance=self.invoice_address, validate_vat_id=False,
all_optional=self.all_optional,
)
else:
return forms.Form(data=self.request.POST if self.request.method == "POST" else None)
f = forms.Form(data=self.request.POST if self.request.method == "POST" else None)
override_sets = self._contact_override_sets
for overrides in override_sets:
for fname, val in overrides.items():
if 'disabled' in val and fname in f.fields:
f.fields[fname].disabled = val['disabled']
return f
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)

View File

@@ -32,7 +32,9 @@ from pretix.presale.forms.checkout import (
)
from pretix.presale.signals import (
checkout_all_optional, checkout_confirm_messages, checkout_flow_steps,
contact_form_fields, order_meta_from_request, question_form_fields,
contact_form_fields, contact_form_fields_overrides,
order_meta_from_request, question_form_fields,
question_form_fields_overrides,
)
from pretix.presale.views import (
CartMixin, get_cart, get_cart_is_free, get_cart_total,
@@ -423,6 +425,16 @@ class QuestionsStep(QuestionsViewMixin, CartMixin, TemplateFlowStep):
return True
return False
@cached_property
def _contact_override_sets(self):
return [
resp for recv, resp in contact_form_fields_overrides.send(
self.request.event,
request=self.request,
order=None,
)
]
@cached_property
def contact_form(self):
wd = self.cart_session.get('widget_data', {})
@@ -433,14 +445,36 @@ class QuestionsStep(QuestionsViewMixin, CartMixin, TemplateFlowStep):
)
}
initial.update(self.cart_session.get('contact_form_data', {}))
override_sets = self._contact_override_sets
for overrides in override_sets:
initial.update({
k: v['initial'] for k, v in overrides.items() if 'initial' in v
})
f = ContactForm(data=self.request.POST if self.request.method == "POST" else None,
event=self.request.event,
request=self.request,
initial=initial, all_optional=self.all_optional)
if wd.get('email', '') and wd.get('fix', '') == "true":
f.fields['email'].disabled = True
for overrides in override_sets:
for fname, val in overrides.items():
if 'disabled' in val and fname in f.fields:
f.fields[fname].disabled = val['disabled']
return f
def get_question_override_sets(self, cart_position):
return [
resp for recv, resp in question_form_fields_overrides.send(
self.request.event,
position=cart_position,
request=self.request
)
]
@cached_property
def eu_reverse_charge_relevant(self):
return any([p.item.tax_rule and (p.item.tax_rule.eu_reverse_charge or p.item.tax_rule.custom_rules)
@@ -466,6 +500,13 @@ class QuestionsStep(QuestionsViewMixin, CartMixin, TemplateFlowStep):
else:
wd_initial = {}
initial = dict(wd_initial)
override_sets = self._contact_override_sets
for overrides in override_sets:
initial.update({
k: v['initial'] for k, v in overrides.items() if 'initial' in v
})
if not self.address_asked and self.request.event.settings.invoice_name_required:
f = InvoiceNameForm(data=self.request.POST if self.request.method == "POST" else None,
event=self.request.event,
@@ -483,6 +524,12 @@ class QuestionsStep(QuestionsViewMixin, CartMixin, TemplateFlowStep):
for name, field in f.fields.items():
if wd_initial.get(name) and wd.get('fix', '') == 'true':
field.disabled = True
for overrides in override_sets:
for fname, val in overrides.items():
if 'disabled' in val and fname in f.fields:
f.fields[fname].disabled = val['disabled']
return f
@cached_property

View File

@@ -180,6 +180,21 @@ As with all plugin signals, the ``sender`` keyword argument will contain the eve
argument will contain the request object.
"""
contact_form_fields_overrides = EventPluginSignal(
providing_args=["request", "order"]
)
"""
This signal allows you to override fields of the contact form that is presented during checkout
and by default only asks for the email address. It is also being used for the invoice address
form. You are supposed to return a dictionary of dictionaries with globally unique keys. The
value-dictionary should contain one or more of the following keys: ``initial``, ``disabled``. The
key of the dictionary should be the name of the form field.
As with all plugin signals, the ``sender`` keyword argument will contain the event. A ``request``
argument will contain the request object. The ``order`` argument is ``None`` during the checkout
process and contains an order if the customer is trying to change an existing order.
"""
question_form_fields = EventPluginSignal(
providing_args=["position"]
)
@@ -196,6 +211,22 @@ later.
As with all plugin signals, the ``sender`` keyword argument will contain the event.
"""
question_form_fields_overrides = EventPluginSignal(
providing_args=["position", "request"]
)
"""
This signal allows you to override fields of the questions form that is presented during checkout
and by default only asks for the questions configured in the backend. You are supposed to return a
dictionary of dictionaries with globally unique keys. The value-dictionary should contain one or
more of the following keys: ``initial``, ``disabled``. The key of the dictionary should not be the
question's form field name (``question_n``) but rather the questions ``identifier``.
The ``position`` keyword argument will contain a ``CartPosition`` or ``OrderPosition`` object.
As with all plugin signals, the ``sender`` keyword argument will contain the event. A ``request``
argument will contain the request object.
"""
order_info = EventPluginSignal(
providing_args=["order", "request"]
)

View File

@@ -50,6 +50,7 @@ from pretix.helpers.safedownload import check_token
from pretix.multidomain.urlreverse import build_absolute_uri, eventreverse
from pretix.presale.forms.checkout import InvoiceAddressForm, QuestionsForm
from pretix.presale.forms.order import OrderPositionChangeForm
from pretix.presale.signals import question_form_fields_overrides
from pretix.presale.views import (
CartMixin, EventViewMixin, iframe_entry_view_wrapper,
)
@@ -673,6 +674,20 @@ class OrderModify(EventViewMixin, OrderDetailMixin, OrderQuestionsViewMixin, Tem
return []
return super().positions
def get_question_override_sets(self, order_position):
override_sets = [
resp for recv, resp in question_form_fields_overrides.send(
self.request.event,
position=order_position,
request=self.request
)
]
for override in override_sets:
for k in override:
# We don't want initial values to be modified, they should come from the order directly
override[k].pop('initial', None)
return override_sets
def post(self, request, *args, **kwargs):
failed = not self.save() or not self.invoice_form.is_valid()
if failed: