Mail settings: Correctly declare plaintext email (Z#23218835) (#5738)

* Mail settings: Correctly declare plaintext email (Z#23218835)

* Apply suggestions from code review

Co-authored-by: luelista <weller@rami.io>

* Update escaping

* Escaping update

---------

Co-authored-by: luelista <weller@rami.io>
This commit is contained in:
Raphael Michel
2026-01-05 12:33:43 +01:00
committed by GitHub
parent aa5f635932
commit 308e14bab3
3 changed files with 32 additions and 9 deletions

View File

@@ -801,7 +801,11 @@ def get_sample_context(event, context_parameters, rich=True):
sample = v.render_sample(event) sample = v.render_sample(event)
if isinstance(sample, PlainHtmlAlternativeString): if isinstance(sample, PlainHtmlAlternativeString):
context_dict[k] = PlainHtmlAlternativeString( context_dict[k] = PlainHtmlAlternativeString(
sample.plain, '<{el} class="placeholder" title="{title}">{plain}</{el}>'.format(
el='span',
title=lbl,
plain=escape(sample.plain),
),
'<{el} class="placeholder placeholder-html" title="{title}">{html}</{el}>'.format( '<{el} class="placeholder placeholder-html" title="{title}">{html}</{el}>'.format(
el='div' if sample.is_block else 'span', el='div' if sample.is_block else 'span',
title=lbl, title=lbl,

View File

@@ -45,7 +45,7 @@ from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
from django.db.models import Prefetch, Q, prefetch_related_objects from django.db.models import Prefetch, Q, prefetch_related_objects
from django.forms import formset_factory, inlineformset_factory from django.forms import formset_factory, inlineformset_factory
from django.urls import reverse from django.urls import reverse
from django.utils.functional import cached_property from django.utils.functional import cached_property, lazy
from django.utils.html import escape, format_html from django.utils.html import escape, format_html
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.timezone import get_current_timezone_name from django.utils.timezone import get_current_timezone_name
@@ -53,7 +53,7 @@ from django.utils.translation import gettext, gettext_lazy as _, pgettext_lazy
from django_countries.fields import LazyTypedChoiceField from django_countries.fields import LazyTypedChoiceField
from django_scopes.forms import SafeModelMultipleChoiceField from django_scopes.forms import SafeModelMultipleChoiceField
from i18nfield.forms import ( from i18nfield.forms import (
I18nForm, I18nFormField, I18nFormSetMixin, I18nTextInput, I18nForm, I18nFormField, I18nFormSetMixin, I18nTextarea, I18nTextInput,
) )
from pytz import common_timezones from pytz import common_timezones
@@ -1311,9 +1311,17 @@ class MailSettingsForm(FormPlaceholderMixin, SettingsForm):
mail_text_order_invoice = I18nFormField( mail_text_order_invoice = I18nFormField(
label=_("Text"), label=_("Text"),
required=False, required=False,
widget=I18nMarkdownTextarea, widget=I18nTextarea, # no Markdown supported
help_text=_("This will only be used if the invoice is sent to a different email address or at a different time " help_text=lazy(
"than the order confirmation."), lambda: str(_(
"This will only be used if the invoice is sent to a different email address or at a different time "
"than the order confirmation."
)) + " " + str(_(
"Formatting is not supported, as some accounting departments process mail automatically and do not "
"handle formatted emails properly."
)),
str
)()
) )
mail_subject_download_reminder = I18nFormField( mail_subject_download_reminder = I18nFormField(
label=_("Subject sent to order contact address"), label=_("Subject sent to order contact address"),
@@ -1481,6 +1489,9 @@ class MailSettingsForm(FormPlaceholderMixin, SettingsForm):
'mail_subject_resend_all_links': ['event', 'orders'], 'mail_subject_resend_all_links': ['event', 'orders'],
'mail_attach_ical_description': ['event', 'event_or_subevent'], 'mail_attach_ical_description': ['event', 'event_or_subevent'],
} }
plain_rendering = {
'mail_text_order_invoice',
}
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.event = event = kwargs.get('obj') self.event = event = kwargs.get('obj')
@@ -1499,7 +1510,7 @@ class MailSettingsForm(FormPlaceholderMixin, SettingsForm):
self.event.meta_values_cached = self.event.meta_values.select_related('property').all() self.event.meta_values_cached = self.event.meta_values.select_related('property').all()
for k, v in self.base_context.items(): for k, v in self.base_context.items():
self._set_field_placeholders(k, v, rich=k.startswith('mail_text_')) self._set_field_placeholders(k, v, rich=k.startswith('mail_text_') and k not in self.plain_rendering)
for k, v in list(self.fields.items()): for k, v in list(self.fields.items()):
if k.endswith('_attendee') and not event.settings.attendee_emails_asked: if k.endswith('_attendee') and not event.settings.attendee_emails_asked:

View File

@@ -829,8 +829,8 @@ class MailSettingsPreview(EventPermissionRequiredMixin, View):
return locales return locales
# get all supported placeholders with dummy values # get all supported placeholders with dummy values
def placeholders(self, item): def placeholders(self, item, rich=True):
return get_sample_context(self.request.event, MailSettingsForm.base_context[item]) return get_sample_context(self.request.event, MailSettingsForm.base_context[item], rich=rich)
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
preview_item = request.POST.get('item', '') preview_item = request.POST.get('item', '')
@@ -851,6 +851,14 @@ class MailSettingsPreview(EventPermissionRequiredMixin, View):
msgs[self.supported_locale[idx]] = prefix_subject(self.request.event, 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 bleach.clean(v), self.placeholders(preview_item), raise_on_missing=True
), highlight=True) ), highlight=True)
elif preview_item in MailSettingsForm.plain_rendering:
msgs[self.supported_locale[idx]] = mark_safe(
format_map(
conditional_escape(v),
self.placeholders(preview_item, rich=False),
raise_on_missing=True
).replace("\n", "<br />")
)
else: else:
placeholders = self.placeholders(preview_item) placeholders = self.placeholders(preview_item)
msgs[self.supported_locale[idx]] = format_map( msgs[self.supported_locale[idx]] = format_map(