mirror of
https://github.com/pretix/pretix.git
synced 2026-05-08 15:44:02 +00:00
Mark strings as formatted to prevent double-formatting
This commit is contained in:
@@ -87,7 +87,7 @@ from pretix.base.timemachine import time_machine_now
|
|||||||
|
|
||||||
from ...helpers import OF_SELF
|
from ...helpers import OF_SELF
|
||||||
from ...helpers.countries import CachedCountries, FastCountryField
|
from ...helpers.countries import CachedCountries, FastCountryField
|
||||||
from ...helpers.format import format_map
|
from ...helpers.format import FormattedString, format_map
|
||||||
from ...helpers.names import build_name
|
from ...helpers.names import build_name
|
||||||
from ...testutils.middleware import debugflags_var
|
from ...testutils.middleware import debugflags_var
|
||||||
from ._transactions import (
|
from ._transactions import (
|
||||||
@@ -1178,7 +1178,8 @@ class Order(LockModel, LoggedModel):
|
|||||||
recipient = position.attendee_email
|
recipient = position.attendee_email
|
||||||
|
|
||||||
email_content = render_mail(template, context)
|
email_content = render_mail(template, context)
|
||||||
subject = format_map(subject, context)
|
if not isinstance(subject, FormattedString):
|
||||||
|
subject = format_map(subject, context)
|
||||||
mail(
|
mail(
|
||||||
recipient, subject, template, context,
|
recipient, subject, template, context,
|
||||||
self.event, self.locale, self, headers=headers, sender=sender,
|
self.event, self.locale, self, headers=headers, sender=sender,
|
||||||
@@ -2907,7 +2908,8 @@ class OrderPosition(AbstractPosition):
|
|||||||
with language(self.order.locale, self.order.event.settings.region):
|
with language(self.order.locale, self.order.event.settings.region):
|
||||||
recipient = self.attendee_email
|
recipient = self.attendee_email
|
||||||
email_content = render_mail(template, context)
|
email_content = render_mail(template, context)
|
||||||
subject = format_map(subject, context)
|
if not isinstance(subject, FormattedString):
|
||||||
|
subject = format_map(subject, context)
|
||||||
mail(
|
mail(
|
||||||
recipient, subject, template, context,
|
recipient, subject, template, context,
|
||||||
self.event, self.order.locale, order=self.order, headers=headers, sender=sender,
|
self.event, self.order.locale, order=self.order, headers=headers, sender=sender,
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ from pretix.base.signals import (
|
|||||||
)
|
)
|
||||||
from pretix.celery_app import app
|
from pretix.celery_app import app
|
||||||
from pretix.helpers import OF_SELF
|
from pretix.helpers import OF_SELF
|
||||||
from pretix.helpers.format import SafeFormatter, format_map
|
from pretix.helpers.format import FormattedString, SafeFormatter, format_map
|
||||||
from pretix.helpers.hierarkey import clean_filename
|
from pretix.helpers.hierarkey import clean_filename
|
||||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
from pretix.multidomain.urlreverse import build_absolute_uri
|
||||||
from pretix.presale.ical import get_private_icals
|
from pretix.presale.ical import get_private_icals
|
||||||
@@ -218,6 +218,9 @@ def mail(email: Union[str, Sequence[str]], subject: str, template: Union[str, La
|
|||||||
if email == INVALID_ADDRESS:
|
if email == INVALID_ADDRESS:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if isinstance(template, FormattedString):
|
||||||
|
raise TypeError("Cannot pass an already formatted body template")
|
||||||
|
|
||||||
if no_order_links and not plain_text_only:
|
if no_order_links and not plain_text_only:
|
||||||
raise ValueError('If you set no_order_links, you also need to set plain_text_only.')
|
raise ValueError('If you set no_order_links, you also need to set plain_text_only.')
|
||||||
|
|
||||||
@@ -267,7 +270,8 @@ def mail(email: Union[str, Sequence[str]], subject: str, template: Union[str, La
|
|||||||
body_plain = format_map(body_plain, context, mode=SafeFormatter.MODE_RICH_TO_PLAIN)
|
body_plain = format_map(body_plain, context, mode=SafeFormatter.MODE_RICH_TO_PLAIN)
|
||||||
|
|
||||||
# Build subject
|
# Build subject
|
||||||
subject = format_map(subject, context)
|
if not isinstance(subject, FormattedString):
|
||||||
|
subject = format_map(subject, context)
|
||||||
|
|
||||||
subject = raw_subject = subject.replace('\n', ' ').replace('\r', '')[:900]
|
subject = raw_subject = subject.replace('\n', ' ').replace('\r', '')[:900]
|
||||||
if settings_holder:
|
if settings_holder:
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
import logging
|
import logging
|
||||||
from string import Formatter
|
from string import Formatter
|
||||||
|
|
||||||
|
from django.core.exceptions import SuspiciousOperation
|
||||||
from django.utils.html import conditional_escape
|
from django.utils.html import conditional_escape
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -37,6 +38,17 @@ class PlainHtmlAlternativeString:
|
|||||||
return f"PlainHtmlAlternativeString('{self.plain}', '{self.html}')"
|
return f"PlainHtmlAlternativeString('{self.plain}', '{self.html}')"
|
||||||
|
|
||||||
|
|
||||||
|
class FormattedString(str):
|
||||||
|
"""
|
||||||
|
A str subclass that has been specifically marked as "already formatted" for email rendering
|
||||||
|
purposes to avoid duplicate formatting.
|
||||||
|
"""
|
||||||
|
__slots__ = ()
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
class SafeFormatter(Formatter):
|
class SafeFormatter(Formatter):
|
||||||
"""
|
"""
|
||||||
Customized version of ``str.format`` that (a) behaves just like ``str.format_map`` and
|
Customized version of ``str.format`` that (a) behaves just like ``str.format_map`` and
|
||||||
@@ -78,7 +90,11 @@ class SafeFormatter(Formatter):
|
|||||||
return super().format_field(self._prepare_value(value), '')
|
return super().format_field(self._prepare_value(value), '')
|
||||||
|
|
||||||
|
|
||||||
def format_map(template, context, raise_on_missing=False, mode=SafeFormatter.MODE_RICH_TO_PLAIN, linkifier=None):
|
def format_map(template, context, raise_on_missing=False, mode=SafeFormatter.MODE_RICH_TO_PLAIN, linkifier=None) -> FormattedString:
|
||||||
|
if isinstance(template, FormattedString):
|
||||||
|
raise SuspiciousOperation("Calling format_map() on an already formatted string is likely unsafe.")
|
||||||
if not isinstance(template, str):
|
if not isinstance(template, str):
|
||||||
template = str(template)
|
template = str(template)
|
||||||
return SafeFormatter(context, raise_on_missing, mode=mode, linkifier=linkifier).format(template)
|
return FormattedString(
|
||||||
|
SafeFormatter(context, raise_on_missing, mode=mode, linkifier=linkifier).format(template)
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user