diff --git a/src/pretix/api/serializers/event.py b/src/pretix/api/serializers/event.py index 7c1c739d45..7d1e4eadf1 100644 --- a/src/pretix/api/serializers/event.py +++ b/src/pretix/api/serializers/event.py @@ -565,7 +565,7 @@ class EventSettingsSerializer(serializers.Serializer): 'attendee_addresses_required', 'attendee_company_asked', 'attendee_company_required', - 'confirm_text', + 'confirm_texts', 'order_email_asked_twice', 'payment_term_days', 'payment_term_last', diff --git a/src/pretix/base/migrations/0160_multiple_confirm_texts.py b/src/pretix/base/migrations/0160_multiple_confirm_texts.py new file mode 100644 index 0000000000..60e05ac620 --- /dev/null +++ b/src/pretix/base/migrations/0160_multiple_confirm_texts.py @@ -0,0 +1,27 @@ +# Generated by Django 3.0.8 on 2020-07-31 10:05 + +import json + +from django.db import migrations + + +def migrate_confirm_text(apps, schema_editor): + # We now allow creating multiple confirm texts so we migrate the setting for that + # from `confirm_text` to `confirm_texts` + Event_SettingsStore = apps.get_model('pretixbase', 'Event_SettingsStore') + for store in Event_SettingsStore.objects.filter(key="confirm_text"): + values = json.dumps([json.loads(store.value)]) # convert single value to one-element list + store.key = "confirm_texts" + store.value = values + store.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('pretixbase', '0159_mails_by_sales_channel'), + ] + + operations = [ + migrations.RunPython(migrate_confirm_text, migrations.RunPython.noop), + ] diff --git a/src/pretix/base/reldate.py b/src/pretix/base/reldate.py index 2230b0d0fc..a057884582 100644 --- a/src/pretix/base/reldate.py +++ b/src/pretix/base/reldate.py @@ -35,7 +35,7 @@ class RelativeDateWrapper: def __init__(self, data: Union[datetime.datetime, RelativeDate]): self.data = data - def date(self, event) -> datetime.datetime: + def date(self, event) -> datetime.date: from .models import SubEvent if isinstance(self.data, datetime.date): @@ -81,8 +81,8 @@ class RelativeDateWrapper: second=self.data.time.second ) new_date = new_date.astimezone(tz) - newoffset = new_date.utcoffset() - new_date += oldoffset - newoffset + new_offset = new_date.utcoffset() + new_date += oldoffset - new_offset return new_date def to_string(self) -> str: diff --git a/src/pretix/base/services/cart.py b/src/pretix/base/services/cart.py index 8634a8d29e..22cbdbde07 100644 --- a/src/pretix/base/services/cart.py +++ b/src/pretix/base/services/cart.py @@ -27,7 +27,7 @@ from pretix.base.services.locking import LockTimeoutException, NoLockManager from pretix.base.services.pricing import get_price from pretix.base.services.quotas import QuotaAvailability from pretix.base.services.tasks import ProfiledEventTask -from pretix.base.settings import PERSON_NAME_SCHEMES +from pretix.base.settings import PERSON_NAME_SCHEMES, LazyI18nStringList from pretix.base.signals import validate_cart_addons from pretix.base.templatetags.rich_text import rich_text from pretix.celery_app import app @@ -1240,9 +1240,7 @@ def set_cart_addons(self, event: Event, addons: List[dict], cart_id: str=None, l @receiver(checkout_confirm_messages, dispatch_uid="cart_confirm_messages") def confirm_messages(sender, *args, **kwargs): - if not sender.settings.confirm_text: + if not sender.settings.confirm_texts: return {} - - return { - 'confirm_text': rich_text(str(sender.settings.confirm_text)) - } + confirm_texts = sender.settings.get("confirm_texts", as_type=LazyI18nStringList) + return {'confirm_text_%i' % index: rich_text(str(text)) for index, text in enumerate(confirm_texts)} diff --git a/src/pretix/base/settings.py b/src/pretix/base/settings.py index cadd41c9f8..08b47c8f49 100644 --- a/src/pretix/base/settings.py +++ b/src/pretix/base/settings.py @@ -1,5 +1,6 @@ import json -from collections import OrderedDict +import operator +from collections import OrderedDict, UserList from datetime import datetime from decimal import Decimal from typing import Any @@ -37,6 +38,20 @@ def country_choice_kwargs(): } +class LazyI18nStringList(UserList): + def __init__(self, init_list=None): + super().__init__() + if init_list is not None: + self.data = [v if isinstance(v, LazyI18nString) else LazyI18nString(v) for v in init_list] + + def serialize(self): + return json.dumps([s.data for s in self.data]) + + @classmethod + def unserialize(cls, s): + return cls(json.loads(s)) + + DEFAULTS = { 'max_items_per_order': { 'default': '10', @@ -1127,18 +1142,11 @@ DEFAULTS = { ), 'serializer_class': serializers.URLField, }, - 'confirm_text': { - 'default': None, - 'type': LazyI18nString, - 'form_class': I18nFormField, - 'serializer_class': I18nField, - 'form_kwargs': dict( - label=_('Confirmation text'), - help_text=_('This text needs to be confirmed by the user before a purchase is possible. You could for example ' - 'link your terms of service here. If you use the Pages feature to publish your terms of service, ' - 'you don\'t need this setting since you can configure it there.'), - widget=I18nTextarea, - ) + 'confirm_texts': { + 'default': LazyI18nStringList(), + 'type': LazyI18nStringList, + 'serializer_class': serializers.ListField, + 'serializer_kwargs': lambda: dict(child=I18nField()), }, 'mail_html_renderer': { 'default': 'classic', @@ -1939,6 +1947,9 @@ def i18n_uns(v): settings_hierarkey.add_type(LazyI18nString, serialize=lambda s: json.dumps(s.data), unserialize=i18n_uns) +settings_hierarkey.add_type(LazyI18nStringList, + serialize=operator.methodcaller("serialize"), + unserialize=LazyI18nStringList.unserialize) settings_hierarkey.add_type(RelativeDateWrapper, serialize=lambda rdw: rdw.to_string(), unserialize=lambda s: RelativeDateWrapper.from_string(s)) diff --git a/src/pretix/control/forms/event.py b/src/pretix/control/forms/event.py index e60aa51baa..8b4c6d4be3 100644 --- a/src/pretix/control/forms/event.py +++ b/src/pretix/control/forms/event.py @@ -518,7 +518,6 @@ class EventSettingsForm(SettingsForm): 'attendee_company_required', 'attendee_addresses_asked', 'attendee_addresses_required', - 'confirm_text', 'banner_text', 'banner_text_bottom', 'order_email_asked_twice', @@ -535,11 +534,6 @@ class EventSettingsForm(SettingsForm): def __init__(self, *args, **kwargs): self.event = kwargs['obj'] super().__init__(*args, **kwargs) - self.fields['confirm_text'].widget.attrs['rows'] = '3' - self.fields['confirm_text'].widget.attrs['placeholder'] = _( - 'e.g. I hereby confirm that I have read and agree with the event organizer\'s terms of service ' - 'and agree with them.' - ) self.fields['name_scheme'].choices = ( (k, _('Ask for {fields}, display like {example}').format( fields=' + '.join(str(vv[1]) for vv in v['fields']), @@ -1342,3 +1336,25 @@ class ItemMetaPropertyForm(forms.ModelForm): widgets = { 'default': forms.TextInput() } + + +class ConfirmTextForm(I18nForm): + text = I18nFormField( + widget=I18nTextarea, + widget_kwargs={'attrs': {'rows': '2'}}, + ) + + +class BaseConfirmTextFormSet(I18nFormSetMixin, forms.BaseFormSet): + def __init__(self, *args, **kwargs): + event = kwargs.pop('event', None) + if event: + kwargs['locales'] = event.settings.get('locales') + super().__init__(*args, **kwargs) + + +ConfirmTextFormset = formset_factory( + ConfirmTextForm, + formset=BaseConfirmTextFormSet, + can_order=True, can_delete=True, extra=0 +) diff --git a/src/pretix/control/templates/pretixcontrol/event/settings.html b/src/pretix/control/templates/pretixcontrol/event/settings.html index 7ffdc93376..eac81852cc 100644 --- a/src/pretix/control/templates/pretixcontrol/event/settings.html +++ b/src/pretix/control/templates/pretixcontrol/event/settings.html @@ -26,7 +26,11 @@ {% bootstrap_field form.date_to layout="control" %}
{% bootstrap_field form.location layout="control" %} -
+