From c9e5cce7d01477d52b8e4fd6bd413a197f1631d4 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Wed, 15 Mar 2023 15:55:34 +0100 Subject: [PATCH] Voucher bulk creation: Add markdown preview for email field (#3143) --- .../pretixcontrol/vouchers/bulk.html | 52 +++++++++++++++++-- src/pretix/control/urls.py | 1 + src/pretix/control/views/event.py | 7 ++- src/pretix/control/views/vouchers.py | 50 ++++++++++++++++++ src/pretix/static/pretixcontrol/js/ui/mail.js | 16 ++++-- .../static/pretixcontrol/scss/_forms.scss | 8 ++- 6 files changed, 123 insertions(+), 11 deletions(-) diff --git a/src/pretix/control/templates/pretixcontrol/vouchers/bulk.html b/src/pretix/control/templates/pretixcontrol/vouchers/bulk.html index d5489b3a31..afc33900c3 100644 --- a/src/pretix/control/templates/pretixcontrol/vouchers/bulk.html +++ b/src/pretix/control/templates/pretixcontrol/vouchers/bulk.html @@ -5,7 +5,7 @@ {% block title %}{% trans "Voucher" %}{% endblock %} {% block inside %}

{% trans "Create multiple vouchers" %}

-
+ {% csrf_token %} {% bootstrap_form_errors form %}
@@ -82,8 +82,54 @@
{% trans "Send out emails" %} {% bootstrap_field form.send layout="control" %} - {% bootstrap_field form.send_subject layout="horizontal" %} - {% bootstrap_field form.send_message layout="horizontal" %} + +
+ {% with field=form.send_subject item="send_subject" %} + +
+ +
+
+ {% bootstrap_field field show_label=False form_group_class="" %} +
+
+
+
+
+
+ {% endwith %} +
+
+ {% with field=form.send_message item="send_message" %} + +
+ +
+
+ {% bootstrap_field field show_label=False form_group_class="" %} +
+
+
+
+
+
+ {% endwith %} +
+ {% bootstrap_field form.send_recipients layout="horizontal" %}
{% eventsignal request.event "pretix.control.signals.voucher_form_html" form=form %} diff --git a/src/pretix/control/urls.py b/src/pretix/control/urls.py index ea7196723b..4b77593fc2 100644 --- a/src/pretix/control/urls.py +++ b/src/pretix/control/urls.py @@ -324,6 +324,7 @@ urlpatterns = [ re_path(r'^vouchers/add$', vouchers.VoucherCreate.as_view(), name='event.vouchers.add'), re_path(r'^vouchers/go$', vouchers.VoucherGo.as_view(), name='event.vouchers.go'), re_path(r'^vouchers/bulk_add$', vouchers.VoucherBulkCreate.as_view(), name='event.vouchers.bulk'), + re_path(r'^vouchers/bulk_add/mail_preview$', vouchers.VoucherBulkMailPreview.as_view(), name='event.vouchers.bulk.mail_preview'), re_path(r'^vouchers/bulk_action$', vouchers.VoucherBulkAction.as_view(), name='event.vouchers.bulkaction'), re_path(r'^orders/(?P[0-9A-Z]+)/transition$', orders.OrderTransition.as_view(), name='event.order.transition'), diff --git a/src/pretix/control/views/event.py b/src/pretix/control/views/event.py index 65f3efa8f1..635ae8f0e5 100644 --- a/src/pretix/control/views/event.py +++ b/src/pretix/control/views/event.py @@ -712,8 +712,11 @@ class MailSettingsPreview(EventPermissionRequiredMixin, View): ctx = {} for p in get_available_placeholders(self.request.event, MailSettingsForm.base_context[item]).values(): s = str(p.render_sample(self.request.event)) - if s.strip().startswith('*'): - ctx[p.identifier] = s + if s.strip().startswith('* '): + ctx[p.identifier] = '
{}
'.format( + _('This value will be replaced based on dynamic parameters.'), + markdown_compile_email(s) + ) else: ctx[p.identifier] = '{}'.format( _('This value will be replaced based on dynamic parameters.'), diff --git a/src/pretix/control/views/vouchers.py b/src/pretix/control/views/vouchers.py index 1dfe86f6a0..ee331c79c9 100644 --- a/src/pretix/control/views/vouchers.py +++ b/src/pretix/control/views/vouchers.py @@ -35,6 +35,7 @@ import io +import bleach from defusedcsv import csv from django.conf import settings from django.contrib import messages @@ -56,10 +57,12 @@ from django.views.generic import ( CreateView, ListView, TemplateView, UpdateView, View, ) +from pretix.base.email import get_available_placeholders from pretix.base.models import CartPosition, LogEntry, Voucher from pretix.base.models.vouchers import generate_codes from pretix.base.services.locking import NoLockManager from pretix.base.services.vouchers import vouchers_send +from pretix.base.templatetags.rich_text import markdown_compile_email from pretix.base.views.tasks import AsyncFormView from pretix.control.forms.filter import VoucherFilterForm, VoucherTagFilterForm from pretix.control.forms.vouchers import VoucherBulkForm, VoucherForm @@ -67,6 +70,7 @@ from pretix.control.permissions import EventPermissionRequiredMixin from pretix.control.signals import voucher_form_class from pretix.control.views import PaginationMixin from pretix.helpers.compat import CompatDeleteView +from pretix.helpers.format import format_map from pretix.helpers.models import modelcopy @@ -509,6 +513,52 @@ class VoucherBulkCreate(EventPermissionRequiredMixin, AsyncFormView): return super().form_invalid(form) +class VoucherBulkMailPreview(EventPermissionRequiredMixin, View): + permission = 'can_change_vouchers' + + # return the origin text if key is missing in dict + class SafeDict(dict): + def __missing__(self, key): + return '{' + key + '}' + + # get all supported placeholders with dummy values + def placeholders(self, item): + ctx = {} + base_ctx = ['event', 'name'] + if item == 'send_message': + base_ctx += ['voucher_list'] + for p in get_available_placeholders(self.request.event, base_ctx).values(): + s = str(p.render_sample(self.request.event)) + if s.strip().startswith('* ') or s.startswith(' '): + ctx[p.identifier] = '
{}
'.format( + _('This value will be replaced based on dynamic parameters.'), + markdown_compile_email(s) + ) + else: + ctx[p.identifier] = '{}'.format( + _('This value will be replaced based on dynamic parameters.'), + s + ) + return self.SafeDict(ctx) + + def post(self, request, *args, **kwargs): + preview_item = request.POST.get('item', '') + if preview_item not in ('send_message', 'send_subject'): + return HttpResponseBadRequest(_('invalid item')) + msgs = {} + if "subject" in preview_item: + msgs["all"] = format_map(bleach.clean(request.POST.get(preview_item, "")), self.placeholders(preview_item)) + else: + msgs["all"] = markdown_compile_email( + format_map(request.POST.get(preview_item), self.placeholders(preview_item)) + ) + + return JsonResponse({ + 'item': preview_item, + 'msgs': msgs + }) + + class VoucherRNG(EventPermissionRequiredMixin, View): permission = 'can_change_vouchers' diff --git a/src/pretix/static/pretixcontrol/js/ui/mail.js b/src/pretix/static/pretixcontrol/js/ui/mail.js index 60f3e5b049..06ac66df1f 100644 --- a/src/pretix/static/pretixcontrol/js/ui/mail.js +++ b/src/pretix/static/pretixcontrol/js/ui/mail.js @@ -30,11 +30,9 @@ function preview_task_error(item) { } } -$(function () { - "use strict"; - - $('.mail-preview .placeholder').tooltip(); - $('a[type=preview]').on('click', function () { +function mail_preview_setup($el) { + $el.find('.mail-preview .placeholder').tooltip(); + $el.find('a[type=preview]').on('click', function () { var itemName = $(this).closest('.preview-panel').attr('for'); if ($('#' + itemName + '_panel').data('ajaxing') || $(this).parent('.active').length !== 0) { return; @@ -66,4 +64,12 @@ $(function () { ); }); +} + +$(function () { + "use strict"; + mail_preview_setup($("body")); + $(document).on("pretix:bind-forms", function () { + mail_preview_setup($("body")); + }); }); diff --git a/src/pretix/static/pretixcontrol/scss/_forms.scss b/src/pretix/static/pretixcontrol/scss/_forms.scss index b881aacd44..9f84ef4708 100644 --- a/src/pretix/static/pretixcontrol/scss/_forms.scss +++ b/src/pretix/static/pretixcontrol/scss/_forms.scss @@ -181,7 +181,7 @@ div.mail-preview { border-top-width: 0; margin-bottom: 0; padding-right: 15px; - padding-bottom: 15px; + padding-bottom: 8px; &:first-child { @include border-top-radius($input-border-radius); @@ -217,6 +217,12 @@ div.mail-preview { p:last-child { margin-bottom: 0; } + /* Reset styling from bootstrap that we don't actually have in emails */ + pre { + background: none; + border: none; + padding: 0; + } } .search-line {