mirror of
https://github.com/pretix/pretix.git
synced 2026-05-09 15:54:03 +00:00
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:
@@ -34,7 +34,7 @@ Frontend
|
|||||||
--------
|
--------
|
||||||
|
|
||||||
.. automodule:: pretix.presale.signals
|
.. automodule:: pretix.presale.signals
|
||||||
:members: html_head, html_footer, footer_link, front_page_top, front_page_bottom, front_page_bottom_widget, fee_calculation_for_cart, contact_form_fields, question_form_fields, checkout_confirm_messages, checkout_confirm_page_content, checkout_all_optional, html_page_header, sass_preamble, sass_postamble, render_seating_plan, checkout_flow_steps, position_info, position_info_top, item_description, global_html_head, global_html_footer, global_html_page_header
|
:members: html_head, html_footer, footer_link, front_page_top, front_page_bottom, front_page_bottom_widget, fee_calculation_for_cart, contact_form_fields, question_form_fields, contact_form_fields_overrides, question_form_fields_overrides, checkout_confirm_messages, checkout_confirm_page_content, checkout_all_optional, html_page_header, sass_preamble, sass_postamble, render_seating_plan, checkout_flow_steps, position_info, position_info_top, item_description, global_html_head, global_html_footer, global_html_page_header
|
||||||
|
|
||||||
|
|
||||||
.. automodule:: pretix.presale.signals
|
.. automodule:: pretix.presale.signals
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ from pretix.base.models import (
|
|||||||
CartPosition, InvoiceAddress, OrderPosition, Question, QuestionAnswer,
|
CartPosition, InvoiceAddress, OrderPosition, Question, QuestionAnswer,
|
||||||
QuestionOption,
|
QuestionOption,
|
||||||
)
|
)
|
||||||
|
from pretix.presale.signals import contact_form_fields_overrides
|
||||||
|
|
||||||
|
|
||||||
class BaseQuestionsViewMixin:
|
class BaseQuestionsViewMixin:
|
||||||
@@ -34,6 +35,9 @@ class BaseQuestionsViewMixin:
|
|||||||
def _positions_for_questions(self):
|
def _positions_for_questions(self):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def get_question_override_sets(self, position):
|
||||||
|
return []
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def forms(self):
|
def forms(self):
|
||||||
"""
|
"""
|
||||||
@@ -62,6 +66,23 @@ class BaseQuestionsViewMixin:
|
|||||||
self.request.event.settings.attendee_addresses_asked
|
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:
|
if len(form.fields) > 0:
|
||||||
formlist.append(form)
|
formlist.append(form)
|
||||||
return formlist
|
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
|
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
|
@cached_property
|
||||||
def invoice_form(self):
|
def invoice_form(self):
|
||||||
if not self.address_asked and self.request.event.settings.invoice_name_required:
|
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,
|
data=self.request.POST if self.request.method == "POST" else None,
|
||||||
event=self.request.event,
|
event=self.request.event,
|
||||||
instance=self.invoice_address, validate_vat_id=False,
|
instance=self.invoice_address, validate_vat_id=False,
|
||||||
all_optional=self.all_optional
|
all_optional=self.all_optional
|
||||||
)
|
)
|
||||||
if self.address_asked:
|
elif self.address_asked:
|
||||||
return self.invoice_form_class(
|
f = self.invoice_form_class(
|
||||||
data=self.request.POST if self.request.method == "POST" else None,
|
data=self.request.POST if self.request.method == "POST" else None,
|
||||||
event=self.request.event,
|
event=self.request.event,
|
||||||
instance=self.invoice_address, validate_vat_id=False,
|
instance=self.invoice_address, validate_vat_id=False,
|
||||||
all_optional=self.all_optional,
|
all_optional=self.all_optional,
|
||||||
)
|
)
|
||||||
else:
|
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):
|
def get_context_data(self, **kwargs):
|
||||||
ctx = super().get_context_data(**kwargs)
|
ctx = super().get_context_data(**kwargs)
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ from pretix.presale.forms.checkout import (
|
|||||||
)
|
)
|
||||||
from pretix.presale.signals import (
|
from pretix.presale.signals import (
|
||||||
checkout_all_optional, checkout_confirm_messages, checkout_flow_steps,
|
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 (
|
from pretix.presale.views import (
|
||||||
CartMixin, get_cart, get_cart_is_free, get_cart_total,
|
CartMixin, get_cart, get_cart_is_free, get_cart_total,
|
||||||
@@ -423,6 +425,16 @@ class QuestionsStep(QuestionsViewMixin, CartMixin, TemplateFlowStep):
|
|||||||
return True
|
return True
|
||||||
return False
|
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
|
@cached_property
|
||||||
def contact_form(self):
|
def contact_form(self):
|
||||||
wd = self.cart_session.get('widget_data', {})
|
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', {}))
|
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,
|
f = ContactForm(data=self.request.POST if self.request.method == "POST" else None,
|
||||||
event=self.request.event,
|
event=self.request.event,
|
||||||
request=self.request,
|
request=self.request,
|
||||||
initial=initial, all_optional=self.all_optional)
|
initial=initial, all_optional=self.all_optional)
|
||||||
if wd.get('email', '') and wd.get('fix', '') == "true":
|
if wd.get('email', '') and wd.get('fix', '') == "true":
|
||||||
f.fields['email'].disabled = 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
|
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
|
@cached_property
|
||||||
def eu_reverse_charge_relevant(self):
|
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)
|
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:
|
else:
|
||||||
wd_initial = {}
|
wd_initial = {}
|
||||||
initial = dict(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:
|
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,
|
f = InvoiceNameForm(data=self.request.POST if self.request.method == "POST" else None,
|
||||||
event=self.request.event,
|
event=self.request.event,
|
||||||
@@ -483,6 +524,12 @@ class QuestionsStep(QuestionsViewMixin, CartMixin, TemplateFlowStep):
|
|||||||
for name, field in f.fields.items():
|
for name, field in f.fields.items():
|
||||||
if wd_initial.get(name) and wd.get('fix', '') == 'true':
|
if wd_initial.get(name) and wd.get('fix', '') == 'true':
|
||||||
field.disabled = 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
|
return f
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
|
|||||||
@@ -180,6 +180,21 @@ As with all plugin signals, the ``sender`` keyword argument will contain the eve
|
|||||||
argument will contain the request object.
|
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(
|
question_form_fields = EventPluginSignal(
|
||||||
providing_args=["position"]
|
providing_args=["position"]
|
||||||
)
|
)
|
||||||
@@ -196,6 +211,22 @@ later.
|
|||||||
As with all plugin signals, the ``sender`` keyword argument will contain the event.
|
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(
|
order_info = EventPluginSignal(
|
||||||
providing_args=["order", "request"]
|
providing_args=["order", "request"]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ from pretix.helpers.safedownload import check_token
|
|||||||
from pretix.multidomain.urlreverse import build_absolute_uri, eventreverse
|
from pretix.multidomain.urlreverse import build_absolute_uri, eventreverse
|
||||||
from pretix.presale.forms.checkout import InvoiceAddressForm, QuestionsForm
|
from pretix.presale.forms.checkout import InvoiceAddressForm, QuestionsForm
|
||||||
from pretix.presale.forms.order import OrderPositionChangeForm
|
from pretix.presale.forms.order import OrderPositionChangeForm
|
||||||
|
from pretix.presale.signals import question_form_fields_overrides
|
||||||
from pretix.presale.views import (
|
from pretix.presale.views import (
|
||||||
CartMixin, EventViewMixin, iframe_entry_view_wrapper,
|
CartMixin, EventViewMixin, iframe_entry_view_wrapper,
|
||||||
)
|
)
|
||||||
@@ -673,6 +674,20 @@ class OrderModify(EventViewMixin, OrderDetailMixin, OrderQuestionsViewMixin, Tem
|
|||||||
return []
|
return []
|
||||||
return super().positions
|
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):
|
def post(self, request, *args, **kwargs):
|
||||||
failed = not self.save() or not self.invoice_form.is_valid()
|
failed = not self.save() or not self.invoice_form.is_valid()
|
||||||
if failed:
|
if failed:
|
||||||
|
|||||||
Reference in New Issue
Block a user