mirror of
https://github.com/pretix/pretix.git
synced 2025-12-16 15:02:28 +00:00
Compare commits
6 Commits
notificati
...
fix-mail-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36bbd8b5e4 | ||
|
|
2bfacd925a | ||
|
|
795dd64219 | ||
|
|
0882bd9db0 | ||
|
|
d3f1f02beb | ||
|
|
7b0f7439f0 |
@@ -32,13 +32,13 @@
|
|||||||
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations under the License.
|
# License for the specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
import re
|
|
||||||
|
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.core.validators import BaseValidator
|
from django.core.validators import BaseValidator
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from i18nfield.strings import LazyI18nString
|
from i18nfield.strings import LazyI18nString
|
||||||
|
|
||||||
|
from pretix.helpers.format import format_map
|
||||||
|
|
||||||
|
|
||||||
class PlaceholderValidator(BaseValidator):
|
class PlaceholderValidator(BaseValidator):
|
||||||
"""
|
"""
|
||||||
@@ -47,6 +47,12 @@ class PlaceholderValidator(BaseValidator):
|
|||||||
which are not presented in taken list.
|
which are not presented in taken list.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
error_message = _(
|
||||||
|
'There is an error with your placeholder syntax. Please check that the opening "{" and closing "}" curly '
|
||||||
|
'brackets on your placeholders match up. '
|
||||||
|
'Please note: to use literal "{" or "}", you need to double them as "{{" and "}}".'
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self, limit_value):
|
def __init__(self, limit_value):
|
||||||
super().__init__(limit_value)
|
super().__init__(limit_value)
|
||||||
self.limit_value = limit_value
|
self.limit_value = limit_value
|
||||||
@@ -57,22 +63,15 @@ class PlaceholderValidator(BaseValidator):
|
|||||||
self.__call__(v)
|
self.__call__(v)
|
||||||
return
|
return
|
||||||
|
|
||||||
if value.count('{') != value.count('}'):
|
try:
|
||||||
|
format_map(value, {key.strip('{}'): "" for key in self.limit_value}, raise_on_missing=True)
|
||||||
|
except ValueError:
|
||||||
|
raise ValidationError(self.error_message, code='invalid_placeholder_syntax')
|
||||||
|
except KeyError as e:
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
_('Invalid placeholder syntax: You used a different number of "{" than of "}".'),
|
_('Invalid placeholder: {%(value)s}'),
|
||||||
code='invalid_placeholder_syntax',
|
|
||||||
)
|
|
||||||
|
|
||||||
data_placeholders = list(re.findall(r'({[^}]*})', value, re.X))
|
|
||||||
invalid_placeholders = []
|
|
||||||
for placeholder in data_placeholders:
|
|
||||||
if placeholder not in self.limit_value:
|
|
||||||
invalid_placeholders.append(placeholder)
|
|
||||||
if invalid_placeholders:
|
|
||||||
raise ValidationError(
|
|
||||||
_('Invalid placeholder(s): %(value)s'),
|
|
||||||
code='invalid_placeholders',
|
code='invalid_placeholders',
|
||||||
params={'value': ", ".join(invalid_placeholders,)})
|
params={'value': e.args[0]})
|
||||||
|
|
||||||
def clean(self, x):
|
def clean(self, x):
|
||||||
return x
|
return x
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ from i18nfield.utils import I18nJSONEncoder
|
|||||||
|
|
||||||
from pretix.base.channels import get_all_sales_channels
|
from pretix.base.channels import get_all_sales_channels
|
||||||
from pretix.base.email import get_available_placeholders
|
from pretix.base.email import get_available_placeholders
|
||||||
|
from pretix.base.forms import PlaceholderValidator
|
||||||
from pretix.base.models import Event, LogEntry, Order, TaxRule, Voucher
|
from pretix.base.models import Event, LogEntry, Order, TaxRule, Voucher
|
||||||
from pretix.base.models.event import EventMetaValue
|
from pretix.base.models.event import EventMetaValue
|
||||||
from pretix.base.services import tickets
|
from pretix.base.services import tickets
|
||||||
@@ -713,11 +714,6 @@ class MailSettingsSetup(EventPermissionRequiredMixin, MailSettingsSetupView):
|
|||||||
class MailSettingsPreview(EventPermissionRequiredMixin, View):
|
class MailSettingsPreview(EventPermissionRequiredMixin, View):
|
||||||
permission = 'can_change_event_settings'
|
permission = 'can_change_event_settings'
|
||||||
|
|
||||||
# return the origin text if key is missing in dict
|
|
||||||
class SafeDict(dict):
|
|
||||||
def __missing__(self, key):
|
|
||||||
return '{' + key + '}'
|
|
||||||
|
|
||||||
# create index-language mapping
|
# create index-language mapping
|
||||||
@cached_property
|
@cached_property
|
||||||
def supported_locale(self):
|
def supported_locale(self):
|
||||||
@@ -742,7 +738,7 @@ class MailSettingsPreview(EventPermissionRequiredMixin, View):
|
|||||||
_('This value will be replaced based on dynamic parameters.'),
|
_('This value will be replaced based on dynamic parameters.'),
|
||||||
s
|
s
|
||||||
)
|
)
|
||||||
return self.SafeDict(ctx)
|
return ctx
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
preview_item = request.POST.get('item', '')
|
preview_item = request.POST.get('item', '')
|
||||||
@@ -758,12 +754,21 @@ class MailSettingsPreview(EventPermissionRequiredMixin, View):
|
|||||||
idx = matched.group('idx')
|
idx = matched.group('idx')
|
||||||
if idx in self.supported_locale:
|
if idx in self.supported_locale:
|
||||||
with language(self.supported_locale[idx], self.request.event.settings.region):
|
with language(self.supported_locale[idx], self.request.event.settings.region):
|
||||||
if k.startswith('mail_subject_'):
|
try:
|
||||||
msgs[self.supported_locale[idx]] = format_map(bleach.clean(v), self.placeholders(preview_item))
|
if k.startswith('mail_subject_'):
|
||||||
else:
|
msgs[self.supported_locale[idx]] = format_map(
|
||||||
msgs[self.supported_locale[idx]] = markdown_compile_email(
|
bleach.clean(v), self.placeholders(preview_item), raise_on_missing=True
|
||||||
format_map(v, self.placeholders(preview_item))
|
)
|
||||||
)
|
else:
|
||||||
|
msgs[self.supported_locale[idx]] = markdown_compile_email(
|
||||||
|
format_map(v, self.placeholders(preview_item), raise_on_missing=True)
|
||||||
|
)
|
||||||
|
except ValueError:
|
||||||
|
msgs[self.supported_locale[idx]] = '<div class="alert alert-danger">{}</div>'.format(
|
||||||
|
PlaceholderValidator.error_message)
|
||||||
|
except KeyError as e:
|
||||||
|
msgs[self.supported_locale[idx]] = '<div class="alert alert-danger">{}</div>'.format(
|
||||||
|
_('Invalid placeholder: {%(value)s}') % {'value': e.args[0]})
|
||||||
|
|
||||||
return JsonResponse({
|
return JsonResponse({
|
||||||
'item': preview_item,
|
'item': preview_item,
|
||||||
|
|||||||
@@ -30,17 +30,15 @@ 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
|
||||||
(b) does not allow any unwanted shenanigans like attribute access or format specifiers.
|
(b) does not allow any unwanted shenanigans like attribute access or format specifiers.
|
||||||
"""
|
"""
|
||||||
def __init__(self, context):
|
def __init__(self, context, raise_on_missing=False):
|
||||||
self.context = context
|
self.context = context
|
||||||
|
self.raise_on_missing = raise_on_missing
|
||||||
|
|
||||||
def get_field(self, field_name, args, kwargs):
|
def get_field(self, field_name, args, kwargs):
|
||||||
if '.' in field_name or '[' in field_name:
|
return self.get_value(field_name, args, kwargs), field_name
|
||||||
logger.warning(f'Ignored invalid field name "{field_name}"')
|
|
||||||
return ('{' + str(field_name) + '}', field_name)
|
|
||||||
return super().get_field(field_name, args, kwargs)
|
|
||||||
|
|
||||||
def get_value(self, key, args, kwargs):
|
def get_value(self, key, args, kwargs):
|
||||||
if key not in self.context:
|
if not self.raise_on_missing and key not in self.context:
|
||||||
return '{' + str(key) + '}'
|
return '{' + str(key) + '}'
|
||||||
return self.context[key]
|
return self.context[key]
|
||||||
|
|
||||||
@@ -49,7 +47,7 @@ class SafeFormatter(Formatter):
|
|||||||
return super().format_field(value, '')
|
return super().format_field(value, '')
|
||||||
|
|
||||||
|
|
||||||
def format_map(template, context):
|
def format_map(template, context, raise_on_missing=False):
|
||||||
if not isinstance(template, str):
|
if not isinstance(template, str):
|
||||||
template = str(template)
|
template = str(template)
|
||||||
return SafeFormatter(context).format(template)
|
return SafeFormatter(context, raise_on_missing).format(template)
|
||||||
|
|||||||
Reference in New Issue
Block a user