diff --git a/src/pretix/base/forms/widgets.py b/src/pretix/base/forms/widgets.py index 3903b6e95..b35329557 100644 --- a/src/pretix/base/forms/widgets.py +++ b/src/pretix/base/forms/widgets.py @@ -60,6 +60,18 @@ def replace_arabic_numbers(inp): return inp.translate(table) +def format_placeholders_help_text(placeholders, event=None): + placeholders = [(k, v.render_sample(event) if event else v) for k, v in placeholders.items()] + placeholders.sort(key=lambda x: x[0]) + phs = [ + '' % (_("Sample: %s") % v if v else "", k) + for k, v in placeholders + ] + return _('Available placeholders: {list}').format( + list=' '.join(phs) + ) + + class DatePickerWidget(forms.DateInput): def __init__(self, attrs=None, date_format=None): attrs = attrs or {} diff --git a/src/pretix/control/forms/event.py b/src/pretix/control/forms/event.py index 837243bd2..ead65a519 100644 --- a/src/pretix/control/forms/event.py +++ b/src/pretix/control/forms/event.py @@ -62,6 +62,7 @@ from pytz import common_timezones from pretix.base.channels import get_all_sales_channels from pretix.base.email import get_available_placeholders from pretix.base.forms import I18nModelForm, PlaceholderValidator, SettingsForm +from pretix.base.forms.widgets import format_placeholders_help_text from pretix.base.models import Event, Organizer, TaxRule, Team from pretix.base.models.event import EventFooterLink, EventMetaValue, SubEvent from pretix.base.reldate import RelativeDateField, RelativeDateTimeField @@ -1340,19 +1341,14 @@ class MailSettingsForm(SettingsForm): } def _set_field_placeholders(self, fn, base_parameters): - phs = [ - '{%s}' % p - for p in sorted(get_available_placeholders(self.event, base_parameters).keys()) - ] - ht = _('Available placeholders: {list}').format( - list=', '.join(phs) - ) + placeholders = get_available_placeholders(self.event, base_parameters) + ht = format_placeholders_help_text(placeholders, self.event) if self.fields[fn].help_text: self.fields[fn].help_text += ' ' + str(ht) else: self.fields[fn].help_text = ht self.fields[fn].validators.append( - PlaceholderValidator(phs) + PlaceholderValidator(['{%s}' % p for p in placeholders.keys()]) ) def __init__(self, *args, **kwargs): diff --git a/src/pretix/control/forms/orders.py b/src/pretix/control/forms/orders.py index 811003138..47e52b541 100644 --- a/src/pretix/control/forms/orders.py +++ b/src/pretix/control/forms/orders.py @@ -54,7 +54,7 @@ from pretix.base.email import get_available_placeholders from pretix.base.forms import I18nModelForm, PlaceholderValidator from pretix.base.forms.questions import WrappedPhoneNumberPrefixWidget from pretix.base.forms.widgets import ( - DatePickerWidget, SplitDateTimePickerWidget, + DatePickerWidget, SplitDateTimePickerWidget, format_placeholders_help_text, ) from pretix.base.models import ( Invoice, InvoiceAddress, ItemAddOn, Order, OrderFee, OrderPosition, @@ -677,19 +677,14 @@ class OrderMailForm(forms.Form): ) def _set_field_placeholders(self, fn, base_parameters): - phs = [ - '{%s}' % p - for p in sorted(get_available_placeholders(self.order.event, base_parameters).keys()) - ] - ht = _('Available placeholders: {list}').format( - list=', '.join(phs) - ) + placeholders = get_available_placeholders(self.order.event, base_parameters) + ht = format_placeholders_help_text(placeholders, self.order.event) if self.fields[fn].help_text: self.fields[fn].help_text += ' ' + str(ht) else: self.fields[fn].help_text = ht self.fields[fn].validators.append( - PlaceholderValidator(phs) + PlaceholderValidator(['{%s}' % p for p in placeholders.keys()]) ) def __init__(self, *args, **kwargs): @@ -872,19 +867,14 @@ class EventCancelForm(forms.Form): send_waitinglist_message = forms.CharField() def _set_field_placeholders(self, fn, base_parameters): - phs = [ - '{%s}' % p - for p in sorted(get_available_placeholders(self.event, base_parameters).keys()) - ] - ht = _('Available placeholders: {list}').format( - list=', '.join(phs) - ) + placeholders = get_available_placeholders(self.event, base_parameters) + ht = format_placeholders_help_text(placeholders, self.event) if self.fields[fn].help_text: self.fields[fn].help_text += ' ' + str(ht) else: self.fields[fn].help_text = ht self.fields[fn].validators.append( - PlaceholderValidator(phs) + PlaceholderValidator(['{%s}' % p for p in placeholders.keys()]) ) def __init__(self, *args, **kwargs): diff --git a/src/pretix/control/forms/organizer.py b/src/pretix/control/forms/organizer.py index e357f442b..1b4b22478 100644 --- a/src/pretix/control/forms/organizer.py +++ b/src/pretix/control/forms/organizer.py @@ -63,7 +63,9 @@ from pretix.base.forms.questions import ( NamePartsFormField, WrappedPhoneNumberPrefixWidget, get_country_by_locale, get_phone_prefix, ) -from pretix.base.forms.widgets import SplitDateTimePickerWidget +from pretix.base.forms.widgets import ( + SplitDateTimePickerWidget, format_placeholders_help_text, +) from pretix.base.models import ( Customer, Device, EventMetaProperty, Gate, GiftCard, GiftCardAcceptance, Membership, MembershipType, OrderPosition, Organizer, ReusableMedium, Team, @@ -568,19 +570,14 @@ class MailSettingsForm(SettingsForm): return placeholders def _set_field_placeholders(self, fn, base_parameters): - phs = [ - '{%s}' % p - for p in sorted(self._get_sample_context(base_parameters).keys()) - ] - ht = _('Available placeholders: {list}').format( - list=', '.join(phs) - ) + placeholders = self._get_sample_context(base_parameters) + ht = format_placeholders_help_text(placeholders) if self.fields[fn].help_text: self.fields[fn].help_text += ' ' + str(ht) else: self.fields[fn].help_text = ht self.fields[fn].validators.append( - PlaceholderValidator(phs) + PlaceholderValidator(['{%s}' % p for p in placeholders.keys()]) ) def __init__(self, *args, **kwargs): diff --git a/src/pretix/control/forms/vouchers.py b/src/pretix/control/forms/vouchers.py index b315b0946..1bcbfaa48 100644 --- a/src/pretix/control/forms/vouchers.py +++ b/src/pretix/control/forms/vouchers.py @@ -46,6 +46,7 @@ from django_scopes.forms import SafeModelChoiceField from pretix.base.email import get_available_placeholders from pretix.base.forms import I18nModelForm, PlaceholderValidator +from pretix.base.forms.widgets import format_placeholders_help_text from pretix.base.models import Item, Voucher from pretix.control.forms import SplitDateTimeField, SplitDateTimePickerWidget from pretix.control.forms.widgets import Select2, Select2ItemVarQuota @@ -289,19 +290,15 @@ class VoucherBulkForm(VoucherForm): Recipient = namedtuple('Recipient', 'email number name tag') def _set_field_placeholders(self, fn, base_parameters): - phs = [ - '{%s}' % p - for p in sorted(get_available_placeholders(self.instance.event, base_parameters).keys()) - ] - ht = _('Available placeholders: {list}').format( - list=', '.join(phs) - ) + placeholders = get_available_placeholders(self.instance.event, base_parameters) + ht = format_placeholders_help_text(placeholders, self.instance.event) + if self.fields[fn].help_text: self.fields[fn].help_text += ' ' + str(ht) else: self.fields[fn].help_text = ht self.fields[fn].validators.append( - PlaceholderValidator(phs) + PlaceholderValidator(['{%s}' % p for p in placeholders.keys()]) ) class Meta: diff --git a/src/pretix/plugins/banktransfer/payment.py b/src/pretix/plugins/banktransfer/payment.py index b53bb5e9a..9bfe4585f 100644 --- a/src/pretix/plugins/banktransfer/payment.py +++ b/src/pretix/plugins/banktransfer/payment.py @@ -51,6 +51,7 @@ from text_unidecode import unidecode from pretix.base.email import get_available_placeholders, get_email_context from pretix.base.forms import PlaceholderValidator +from pretix.base.forms.widgets import format_placeholders_help_text from pretix.base.i18n import language from pretix.base.models import InvoiceAddress, Order, OrderPayment, OrderRefund from pretix.base.payment import BasePaymentProvider @@ -205,13 +206,10 @@ class BankTransfer(BasePaymentProvider): @property def settings_form_fields(self): - phs = [ - '{%s}' % p - for p in sorted(get_available_placeholders(self.event, ['event', 'order', 'invoice']).keys()) - ] - phs_ht = _('Available placeholders: {list}').format( - list=', '.join(phs) - ) + placeholders = get_available_placeholders(self.event, ['event', 'order', 'invoice']) + phs_ht = format_placeholders_help_text(placeholders, self.event) + + phs = ['{%s}' % p for p in placeholders] more_fields = OrderedDict([ ('invoice_email', forms.BooleanField( diff --git a/src/pretix/plugins/sendmail/forms.py b/src/pretix/plugins/sendmail/forms.py index 4f7fbe466..52dff3e92 100644 --- a/src/pretix/plugins/sendmail/forms.py +++ b/src/pretix/plugins/sendmail/forms.py @@ -44,7 +44,7 @@ from i18nfield.forms import I18nFormField, I18nTextarea, I18nTextInput from pretix.base.email import get_available_placeholders from pretix.base.forms import I18nModelForm, PlaceholderValidator from pretix.base.forms.widgets import ( - SplitDateTimePickerWidget, TimePickerWidget, + SplitDateTimePickerWidget, TimePickerWidget, format_placeholders_help_text, ) from pretix.base.models import CheckinList, Item, Order, SubEvent from pretix.control.forms import CachedFileField, SplitDateTimeField @@ -54,19 +54,14 @@ from pretix.plugins.sendmail.models import Rule class FormPlaceholderMixin: def _set_field_placeholders(self, fn, base_parameters): - phs = [ - '{%s}' % p - for p in sorted(get_available_placeholders(self.event, base_parameters).keys()) - ] - ht = _('Available placeholders: {list}').format( - list=', '.join(phs) - ) + placeholders = get_available_placeholders(self.event, base_parameters) + ht = format_placeholders_help_text(placeholders, self.event) if self.fields[fn].help_text: self.fields[fn].help_text += ' ' + str(ht) else: self.fields[fn].help_text = ht self.fields[fn].validators.append( - PlaceholderValidator(phs) + PlaceholderValidator(['{%s}' % p for p in placeholders.keys()]) ) diff --git a/src/pretix/static/pretixcontrol/js/ui/main.js b/src/pretix/static/pretixcontrol/js/ui/main.js index 9c846b256..a43d8571f 100644 --- a/src/pretix/static/pretixcontrol/js/ui/main.js +++ b/src/pretix/static/pretixcontrol/js/ui/main.js @@ -699,6 +699,38 @@ var form_handlers = function (el) { el.find("input[name*=question], select[name*=question]").change(questions_toggle_dependent); questions_toggle_dependent(); questions_init_photos(el); + + var lastFocusedInput; + $(document).on('focusin', 'input, textarea', function(e) { + lastFocusedInput = e.target; + }).on("click", function(e) { + if (e.target.classList.contains('content-placeholder')) { + var container = e.target.closest(".form-group"); + if (!lastFocusedInput || !container.contains(lastFocusedInput)) { + lastFocusedInput = container.querySelector("input, textarea"); + //lastFocusedInput.selectionStart = lastFocusedInput.selectionEnd = lastFocusedInput.value.length; + } + if (lastFocusedInput) { + var start = lastFocusedInput.selectionStart; + var end = lastFocusedInput.selectionEnd; + var v = lastFocusedInput.value; + var p = e.target.textContent; + var phStart = /\{\w*$/.exec(v.substring(0, start)); + var phEnd = /^\w*\}/.exec(v.substring(end)); + if (phStart) { + start -= phStart[0].length + } + if (phEnd) { + end += phEnd[0].length; + } + + lastFocusedInput.value = v.substring(0, start) + p + v.substring(end); + lastFocusedInput.selectionStart = start; + lastFocusedInput.selectionEnd = start + p.length + lastFocusedInput.focus(); + } + } + }); }; function setup_basics(el) { diff --git a/src/pretix/static/pretixcontrol/scss/_forms.scss b/src/pretix/static/pretixcontrol/scss/_forms.scss index 9f4b3909c..915a040c4 100644 --- a/src/pretix/static/pretixcontrol/scss/_forms.scss +++ b/src/pretix/static/pretixcontrol/scss/_forms.scss @@ -2,6 +2,16 @@ color: $text-muted; } +.content-placeholder { + padding-left: 2px; + padding-right: 2px; + background: $gray-lighter; + border: 0; + border-radius: $border-radius-base; + margin-bottom: 4px; + margin-right: 6px; +} + td > .form-group { margin-bottom: 0; }