diff --git a/src/pretix/base/services/mail.py b/src/pretix/base/services/mail.py index a1746334c6..ae5387ebc6 100644 --- a/src/pretix/base/services/mail.py +++ b/src/pretix/base/services/mail.py @@ -58,6 +58,7 @@ from django.core.mail import ( from django.core.mail.message import SafeMIMEText from django.db import transaction from django.template.loader import get_template +from django.utils.html import escape from django.utils.timezone import now, override from django.utils.translation import gettext as _, pgettext from django_scopes import scope, scopes_disabled @@ -109,6 +110,22 @@ def clean_sender_name(sender_name: str) -> str: return sender_name +def prefix_subject(settings_holder, subject, highlight=False): + prefix = settings_holder.settings.get('mail_prefix') + if prefix and prefix.startswith('[') and prefix.endswith(']'): + prefix = prefix[1:-1] + if prefix: + prefix = f"[{prefix}]" + if highlight: + prefix = '{}'.format( + _('This prefix has been set in your event or organizer settings.'), + escape(prefix) + ) + + subject = f"{prefix} {subject}" + return subject + + def mail(email: Union[str, Sequence[str]], subject: str, template: Union[str, LazyI18nString], context: Dict[str, Any] = None, event: Event = None, locale: str = None, order: Order = None, position: OrderPosition = None, *, headers: dict = None, sender: str = None, organizer: Organizer = None, @@ -240,11 +257,7 @@ def mail(email: Union[str, Sequence[str]], subject: str, template: Union[str, La and settings_holder.settings.contact_mail and not headers.get('Reply-To'): headers['Reply-To'] = settings_holder.settings.contact_mail - prefix = settings_holder.settings.get('mail_prefix') - if prefix and prefix.startswith('[') and prefix.endswith(']'): - prefix = prefix[1:-1] - if prefix: - subject = "[%s] %s" % (prefix, subject) + subject = prefix_subject(settings_holder, subject) body_plain += "\r\n\r\n-- \r\n" diff --git a/src/pretix/control/views/event.py b/src/pretix/control/views/event.py index 5eafa1343c..e8e8382806 100644 --- a/src/pretix/control/views/event.py +++ b/src/pretix/control/views/event.py @@ -98,6 +98,7 @@ from ...base.i18n import language from ...base.models.items import ( Item, ItemCategory, ItemMetaProperty, Question, Quota, ) +from ...base.services.mail import prefix_subject from ...base.settings import LazyI18nStringList from ...helpers.compat import CompatDeleteView from ...helpers.format import format_map @@ -746,9 +747,9 @@ class MailSettingsPreview(EventPermissionRequiredMixin, View): with language(self.supported_locale[idx], self.request.event.settings.region): try: if k.startswith('mail_subject_'): - msgs[self.supported_locale[idx]] = format_map( + msgs[self.supported_locale[idx]] = prefix_subject(self.request.event, format_map( bleach.clean(v), self.placeholders(preview_item), raise_on_missing=True - ) + ), highlight=True) else: msgs[self.supported_locale[idx]] = markdown_compile_email( format_map(v, self.placeholders(preview_item), raise_on_missing=True) diff --git a/src/pretix/control/views/orders.py b/src/pretix/control/views/orders.py index e86fafc417..274f69fa1e 100644 --- a/src/pretix/control/views/orders.py +++ b/src/pretix/control/views/orders.py @@ -62,8 +62,9 @@ from django.urls import reverse from django.utils import formats from django.utils.formats import date_format, get_format from django.utils.functional import cached_property -from django.utils.html import conditional_escape +from django.utils.html import conditional_escape, escape from django.utils.http import url_has_allowed_host_and_scheme +from django.utils.safestring import mark_safe from django.utils.timezone import make_aware, now from django.utils.translation import gettext, gettext_lazy as _, ngettext from django.views.generic import ( @@ -94,7 +95,9 @@ from pretix.base.services.invoices import ( invoice_qualified, regenerate_invoice, ) from pretix.base.services.locking import LockTimeoutException -from pretix.base.services.mail import SendMailException, render_mail +from pretix.base.services.mail import ( + SendMailException, prefix_subject, render_mail, +) from pretix.base.services.orders import ( OrderChangeManager, OrderError, approve_order, cancel_order, deny_order, extend_order, mark_order_expired, mark_order_refunded, @@ -2304,7 +2307,9 @@ class OrderSendMail(EventPermissionRequiredMixin, OrderViewMixin, FormView): email_content = render_mail(email_template, email_context) if self.request.POST.get('action') == 'preview': self.preview_output = { - 'subject': _('Subject: {subject}').format(subject=email_subject), + 'subject': mark_safe(_('Subject: {subject}').format( + subject=prefix_subject(order.event, escape(email_subject), highlight=True) + )), 'html': markdown_compile_email(email_content) } return self.get(self.request, *self.args, **self.kwargs) @@ -2369,7 +2374,9 @@ class OrderPositionSendMail(OrderSendMail): email_content = render_mail(email_template, email_context) if self.request.POST.get('action') == 'preview': self.preview_output = { - 'subject': _('Subject: {subject}').format(subject=email_subject), + 'subject': mark_safe(_('Subject: {subject}').format( + subject=prefix_subject(position.order.event, escape(email_subject), highlight=True)) + ), 'html': markdown_compile_email(email_content) } return self.get(self.request, *self.args, **self.kwargs) diff --git a/src/pretix/control/views/organizer.py b/src/pretix/control/views/organizer.py index b8fed498b1..83aff3dbfb 100644 --- a/src/pretix/control/views/organizer.py +++ b/src/pretix/control/views/organizer.py @@ -90,7 +90,7 @@ from pretix.base.models.orders import CancellationRequest from pretix.base.models.organizer import SalesChannel, TeamAPIToken from pretix.base.payment import PaymentException from pretix.base.services.export import multiexport, scheduled_organizer_export -from pretix.base.services.mail import SendMailException, mail +from pretix.base.services.mail import SendMailException, mail, prefix_subject from pretix.base.signals import register_multievent_data_exporters from pretix.base.templatetags.rich_text import markdown_compile_email from pretix.base.views.tasks import AsyncAction @@ -351,8 +351,11 @@ class MailSettingsPreview(OrganizerPermissionRequiredMixin, View): if idx in self.supported_locale: with language(self.supported_locale[idx], self.request.organizer.settings.region): if k.startswith('mail_subject_'): - msgs[self.supported_locale[idx]] = format_map(bleach.clean(v), - self.placeholders(preview_item)) + msgs[self.supported_locale[idx]] = prefix_subject( + self.request.organizer, + format_map(bleach.clean(v), self.placeholders(preview_item)), + highlight=True, + ) else: msgs[self.supported_locale[idx]] = markdown_compile_email( format_map(v, self.placeholders(preview_item)) diff --git a/src/pretix/control/views/vouchers.py b/src/pretix/control/views/vouchers.py index 640dfbe1b3..8f3fb0bc96 100644 --- a/src/pretix/control/views/vouchers.py +++ b/src/pretix/control/views/vouchers.py @@ -64,6 +64,7 @@ from pretix.base.models import ( CartPosition, LogEntry, Voucher, WaitingListEntry, ) from pretix.base.models.vouchers import generate_codes +from pretix.base.services.mail import prefix_subject 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 @@ -572,7 +573,11 @@ class VoucherBulkMailPreview(EventPermissionRequiredMixin, View): 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)) + msgs["all"] = prefix_subject( + self.request.event, + format_map(bleach.clean(request.POST.get(preview_item, "")), self.placeholders(preview_item)), + highlight=True + ) else: msgs["all"] = markdown_compile_email( format_map(request.POST.get(preview_item), self.placeholders(preview_item)) diff --git a/src/pretix/plugins/sendmail/views.py b/src/pretix/plugins/sendmail/views.py index 30d570ef93..bb9c40e0f6 100644 --- a/src/pretix/plugins/sendmail/views.py +++ b/src/pretix/plugins/sendmail/views.py @@ -61,6 +61,7 @@ from pretix.plugins.sendmail.tasks import ( send_mails_to_orders, send_mails_to_waitinglist, ) +from ...base.services.mail import prefix_subject from ...helpers.format import format_map from ...helpers.models import modelcopy from . import forms @@ -197,7 +198,7 @@ class BaseSenderView(EventPermissionRequiredMixin, FormView): ) subject = bleach.clean(form.cleaned_data['subject'].localize(l), tags=[]) - preview_subject = format_map(subject, context_dict) + preview_subject = prefix_subject(self.request.event, format_map(subject, context_dict), highlight=True) message = form.cleaned_data['message'].localize(l) preview_text = markdown_compile_email(format_map(message, context_dict)) @@ -612,7 +613,7 @@ class CreateRule(EventPermissionRequiredMixin, CreateView): ) subject = bleach.clean(form.cleaned_data['subject'].localize(l), tags=[]) - preview_subject = format_map(subject, context_dict) + preview_subject = prefix_subject(self.request.event, format_map(subject, context_dict), highlight=True) template = form.cleaned_data['template'].localize(l) preview_text = markdown_compile_email(format_map(template, context_dict)) @@ -688,7 +689,7 @@ class UpdateRule(EventPermissionRequiredMixin, UpdateView): ) subject = bleach.clean(self.object.subject.localize(lang), tags=[]) - preview_subject = format_map(subject, placeholders) + preview_subject = prefix_subject(self.request.event, format_map(subject, placeholders), highlight=True) template = self.object.template.localize(lang) preview_text = markdown_compile_email(format_map(template, placeholders))