diff --git a/src/pretix/api/serializers/event.py b/src/pretix/api/serializers/event.py index 774c3f8af..78ba309b0 100644 --- a/src/pretix/api/serializers/event.py +++ b/src/pretix/api/serializers/event.py @@ -574,6 +574,7 @@ class EventSettingsSerializer(serializers.Serializer): 'presale_start_show_date', 'locales', 'locale', + 'region', 'last_order_modification_date', 'show_quota_left', 'waiting_list_enabled', diff --git a/src/pretix/api/serializers/order.py b/src/pretix/api/serializers/order.py index f4b139770..c7ddc7635 100644 --- a/src/pretix/api/serializers/order.py +++ b/src/pretix/api/serializers/order.py @@ -180,7 +180,7 @@ class PdfDataSerializer(serializers.Field): res = {} ev = instance.subevent or instance.order.event - with language(instance.order.locale): + with language(instance.order.locale, instance.order.event.settings.region): # This needs to have some extra performance improvements to avoid creating hundreds of queries when # we serialize a list. diff --git a/src/pretix/api/serializers/organizer.py b/src/pretix/api/serializers/organizer.py index b02f62c3c..c4d65c573 100644 --- a/src/pretix/api/serializers/organizer.py +++ b/src/pretix/api/serializers/organizer.py @@ -1,7 +1,7 @@ from decimal import Decimal from django.db.models import Q -from django.utils.translation import get_language, gettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from hierarkey.proxy import HierarkeyProxy from rest_framework import serializers from rest_framework.exceptions import ValidationError @@ -9,6 +9,7 @@ from rest_framework.exceptions import ValidationError from pretix.api.serializers.i18n import I18nAwareModelSerializer from pretix.api.serializers.order import CompatibleJSONField from pretix.base.auth import get_auth_backends +from pretix.base.i18n import get_language_without_region from pretix.base.models import ( Device, GiftCard, GiftCardTransaction, Organizer, SeatingPlan, Team, TeamAPIToken, TeamInvite, User, @@ -145,7 +146,7 @@ class TeamInviteSerializer(serializers.ModelSerializer): }) }, event=None, - locale=get_language() # TODO: expose? + locale=get_language_without_region() # TODO: expose? ) except SendMailException: pass # Already logged @@ -217,6 +218,7 @@ class OrganizerSettingsSerializer(serializers.Serializer): 'giftcard_length', 'giftcard_expiry_years', 'locales', + 'region', 'event_team_provisioning', 'primary_color', 'theme_color_success', diff --git a/src/pretix/api/views/order.py b/src/pretix/api/views/order.py index b5181c508..4321f84a3 100644 --- a/src/pretix/api/views/order.py +++ b/src/pretix/api/views/order.py @@ -582,7 +582,7 @@ class OrderViewSet(viewsets.ModelViewSet): auth=request.auth, ) - with language(order.locale): + with language(order.locale, self.request.event.settings.region): order_placed.send(self.request.event, order=order) if order.status == Order.STATUS_PAID: order_paid.send(self.request.event, order=order) @@ -886,7 +886,7 @@ class OrderPositionViewSet(mixins.DestroyModelMixin, viewsets.ReadOnlyModelViewS price = get_price(**kwargs) tr = kwargs.get('tax_rule', kwargs.get('item').tax_rule) - with language(data.get('locale') or self.request.event.settings.locale): + with language(data.get('locale') or self.request.event.settings.locale, self.request.event.settings.region): return Response({ 'gross': price.gross, 'gross_formatted': money_filter(price.gross, self.request.event.currency, hide_currency=True), diff --git a/src/pretix/base/email.py b/src/pretix/base/email.py index aaf2e670a..3763384c8 100644 --- a/src/pretix/base/email.py +++ b/src/pretix/base/email.py @@ -115,7 +115,7 @@ class TemplateBasedMailRenderer(BaseHTMLMailRenderer): 'body': body_md, 'subject': str(subject), 'color': settings.PRETIX_PRIMARY_COLOR, - 'rtl': get_language() in settings.LANGUAGES_RTL + 'rtl': get_language() in settings.LANGUAGES_RTL or get_language().split('-')[0] in settings.LANGUAGES_RTL, } if self.event: htmlctx['event'] = self.event diff --git a/src/pretix/base/forms/questions.py b/src/pretix/base/forms/questions.py index a124b9c2b..fcca924a9 100644 --- a/src/pretix/base/forms/questions.py +++ b/src/pretix/base/forms/questions.py @@ -35,7 +35,7 @@ from pretix.base.forms.widgets import ( BusinessBooleanRadio, DatePickerWidget, SplitDateTimePickerWidget, TimePickerWidget, UploadedFileWidget, ) -from pretix.base.i18n import language +from pretix.base.i18n import get_language_without_region, language from pretix.base.models import InvoiceAddress, Question, QuestionOption from pretix.base.models.tax import ( EU_COUNTRIES, cc_to_vat_prefix, is_eu_country, @@ -216,8 +216,8 @@ class WrappedPhoneNumberPrefixWidget(PhoneNumberPrefixWidget): def guess_country(event): # Try to guess the initial country from either the country of the merchant # or the locale. This will hopefully save at least some users some scrolling :) - locale = get_language() - country = event.settings.invoice_address_from_country + locale = get_language_without_region() + country = event.settings.region or event.settings.invoice_address_from_country if not country: valid_countries = countries.countries if '-' in locale: diff --git a/src/pretix/base/i18n.py b/src/pretix/base/i18n.py index d6ed7d2aa..b1d52e45a 100644 --- a/src/pretix/base/i18n.py +++ b/src/pretix/base/i18n.py @@ -66,10 +66,42 @@ class LazyNumber: return number_format(self.value, decimal_pos=self.decimal_pos) +ALLOWED_LANGUAGES = dict(settings.LANGUAGES) + + +def get_language_without_region(lng=None): + """ + Returns the currently active language, but strips what pretix calls a ``region``. For example, + if the currently active language is ``en-us``, you will be returned ``en`` since pretix does not + ship with separate language files for ``en-us``. If the currently active language is ``pt-br``, + you will be returned ``pt-br`` since there are separate language files for ``pt-br``. + + tl;dr: You will be always passed a language that is defined in settings.LANGUAGES. + """ + lng = lng or translation.get_language() or settings.LANGUAGE_CODE + if lng not in ALLOWED_LANGUAGES: + lng = lng.split('-')[0] + if lng not in ALLOWED_LANGUAGES: + lng = settings.LANGUAGE_CODE + return lng + + @contextmanager -def language(lng): +def language(lng, region=None): + """ + Temporarily change the active language to ``lng``. Will automatically be rolled back when the + context manager returns. + + You can optionally pass a "region". For example, if you pass ``en`` as ``lng`` and ``US`` as + ``region``, the active language will be ``en-us``, which will mostly affect date/time + formatting. If you pass a ``lng`` that already contains a region, e.g. ``pt-br``, the ``region`` + attribute will be ignored. + """ _lng = translation.get_language() - translation.activate(lng or settings.LANGUAGE_CODE) + lng = lng or settings.LANGUAGE_CODE + if '-' not in lng and region: + lng += '-' + region.lower() + translation.activate(lng) try: yield finally: diff --git a/src/pretix/base/invoice.py b/src/pretix/base/invoice.py index c3b55709f..8e0d6857f 100644 --- a/src/pretix/base/invoice.py +++ b/src/pretix/base/invoice.py @@ -144,7 +144,7 @@ class BaseReportlabInvoiceRenderer(BaseInvoiceRenderer): def _upper(self, val): # We uppercase labels, but not in every language - if get_language() == 'el': + if get_language().startswith('el'): return val return val.upper() diff --git a/src/pretix/base/middleware.py b/src/pretix/base/middleware.py index f300e72f3..1e96224dd 100644 --- a/src/pretix/base/middleware.py +++ b/src/pretix/base/middleware.py @@ -15,7 +15,8 @@ from django.utils.translation.trans_real import ( parse_accept_lang_header, ) -from pretix.base.settings import GlobalSettingsObject +from pretix.base.i18n import get_language_without_region +from pretix.base.settings import global_settings_object from pretix.multidomain.urlreverse import ( get_event_domain, get_organizer_domain, ) @@ -35,19 +36,30 @@ class LocaleMiddleware(MiddlewareMixin): # Normally, this middleware runs *before* the event is set. However, on event frontend pages it # might be run a second time by pretix.presale.EventMiddleware and in this case the event is already # set and can be taken into account for the decision. - if hasattr(request, 'event') and not request.path.startswith(get_script_prefix() + 'control'): - if language not in request.event.settings.locales: - firstpart = language.split('-')[0] - if firstpart in request.event.settings.locales: - language = firstpart - else: - language = request.event.settings.locale - for lang in request.event.settings.locales: - if lang.startswith(firstpart + '-'): - language = lang - break + if not request.path.startswith(get_script_prefix() + 'control'): + if hasattr(request, 'event'): + if language not in request.event.settings.locales: + firstpart = language.split('-')[0] + if firstpart in request.event.settings.locales: + language = firstpart + else: + language = request.event.settings.locale + for lang in request.event.settings.locales: + if lang.startswith(firstpart + '-'): + language = lang + break + if '-' not in language and request.event.settings.region: + language += '-' + request.event.settings.region + elif hasattr(request, 'organizer'): + if '-' not in language and request.organizer.settings.region: + language += '-' + request.organizer.settings.region + else: + gs = global_settings_object(request) + if '-' not in language and gs.settings.region: + language += '-' + gs.settings.region + translation.activate(language) - request.LANGUAGE_CODE = translation.get_language() + request.LANGUAGE_CODE = get_language_without_region() tzname = None if hasattr(request, 'event'): @@ -192,7 +204,7 @@ class SecurityMiddleware(MiddlewareMixin): resp['P3P'] = 'CP=\"ALL DSP COR CUR ADM TAI OUR IND COM NAV INT\"' img_src = [] - gs = GlobalSettingsObject() + gs = global_settings_object(request) if gs.settings.leaflet_tiles: img_src.append(gs.settings.leaflet_tiles[:gs.settings.leaflet_tiles.index("/", 10)].replace("{s}", "*")) diff --git a/src/pretix/base/models/orders.py b/src/pretix/base/models/orders.py index caf9d4902..ffcd5021b 100644 --- a/src/pretix/base/models/orders.py +++ b/src/pretix/base/models/orders.py @@ -857,7 +857,7 @@ class Order(LockModel, LoggedModel): for k, v in self.event.meta_data.items(): context['meta_' + k] = v - with language(self.locale): + with language(self.locale, self.event.settings.region): recipient = self.email if position and position.attendee_email: recipient = position.attendee_email @@ -890,7 +890,7 @@ class Order(LockModel, LoggedModel): ) def resend_link(self, user=None, auth=None): - with language(self.locale): + with language(self.locale, self.event.settings.region): email_template = self.event.settings.mail_text_resend_link email_context = get_email_context(event=self.event, order=self) email_subject = _('Your order: %(code)s') % {'code': self.code} @@ -1514,7 +1514,7 @@ class OrderPayment(models.Model): def _send_paid_mail_attendee(self, position, user): from pretix.base.services.mail import SendMailException - with language(self.order.locale): + with language(self.order.locale, self.order.event.settings.region): email_template = self.order.event.settings.mail_text_order_paid_attendee email_context = get_email_context(event=self.order.event, order=self.order, position=position) email_subject = _('Event registration confirmed: %(code)s') % {'code': self.order.code} @@ -1532,7 +1532,7 @@ class OrderPayment(models.Model): def _send_paid_mail(self, invoice, user, mail_text): from pretix.base.services.mail import SendMailException - with language(self.order.locale): + with language(self.order.locale, self.order.event.settings.region): email_template = self.order.event.settings.mail_text_order_paid email_context = get_email_context(event=self.order.event, order=self.order, payment_info=mail_text) email_subject = _('Payment received for your order: %(code)s') % {'code': self.order.code} @@ -2104,7 +2104,7 @@ class OrderPosition(AbstractPosition): for k, v in self.event.meta_data.items(): context['meta_' + k] = v - with language(self.order.locale): + with language(self.order.locale, self.order.event.settings.region): recipient = self.attendee_email try: email_content = render_mail(template, context) @@ -2132,7 +2132,7 @@ class OrderPosition(AbstractPosition): def resend_link(self, user=None, auth=None): - with language(self.order.locale): + with language(self.order.locale, self.order.event.settings.region): email_template = self.event.settings.mail_text_resend_link email_context = get_email_context(event=self.order.event, order=self.order, position=self) email_subject = _('Your event registration: %(code)s') % {'code': self.order.code} diff --git a/src/pretix/base/models/waitinglist.py b/src/pretix/base/models/waitinglist.py index 63a549a0a..57def028e 100644 --- a/src/pretix/base/models/waitinglist.py +++ b/src/pretix/base/models/waitinglist.py @@ -125,7 +125,7 @@ class WaitingListEntry(LoggedModel): self.voucher = v self.save() - with language(self.locale): + with language(self.locale, self.event.settings.region): mail( self.email, _('You have been selected from the waitinglist for {event}').format(event=str(self.event)), diff --git a/src/pretix/base/pdf.py b/src/pretix/base/pdf.py index 5b3ab4810..5913ab04d 100644 --- a/src/pretix/base/pdf.py +++ b/src/pretix/base/pdf.py @@ -421,6 +421,7 @@ class Renderer: self.layout = layout self.background_file = background_file self.variables = get_variables(event) + self.event = event if self.background_file: self.bg_bytes = self.background_file.read() self.bg_pdf = PdfFileReader(BytesIO(self.bg_bytes), strict=False) @@ -487,7 +488,7 @@ class Renderer: def _get_text_content(self, op: OrderPosition, order: Order, o: dict, inner=False): if o.get('locale', None) and not inner: - with language(o['locale']): + with language(o['locale'], self.event.settings.region): return self._get_text_content(op, order, o, True) ev = self._get_ev(op, order) diff --git a/src/pretix/base/services/cancelevent.py b/src/pretix/base/services/cancelevent.py index ef2dcc87c..300c0a5af 100644 --- a/src/pretix/base/services/cancelevent.py +++ b/src/pretix/base/services/cancelevent.py @@ -24,7 +24,7 @@ logger = logging.getLogger(__name__) def _send_wle_mail(wle: WaitingListEntry, subject: LazyI18nString, message: LazyI18nString, subevent: SubEvent): - with language(wle.locale): + with language(wle.locale, wle.event.settings.region): email_context = get_email_context(event_or_subevent=subevent or wle.event, event=wle.event) try: mail( @@ -41,7 +41,7 @@ def _send_wle_mail(wle: WaitingListEntry, subject: LazyI18nString, message: Lazy def _send_mail(order: Order, subject: LazyI18nString, message: LazyI18nString, subevent: SubEvent, refund_amount: Decimal, user: User, positions: list): - with language(order.locale): + with language(order.locale, order.event.settings.region): try: ia = order.invoice_address except InvoiceAddress.DoesNotExist: diff --git a/src/pretix/base/services/export.py b/src/pretix/base/services/export.py index b993d30bb..6355e2d7f 100644 --- a/src/pretix/base/services/export.py +++ b/src/pretix/base/services/export.py @@ -32,7 +32,7 @@ def export(self, event: Event, fileid: str, provider: str, form_data: Dict[str, ) file = CachedFile.objects.get(id=fileid) - with language(event.settings.locale), override(event.settings.timezone): + with language(event.settings.locale, event.settings.region), override(event.settings.timezone): responses = register_data_exporters.send(event) for receiver, response in responses: ex = response(event, set_progress) @@ -67,15 +67,18 @@ def multiexport(self, organizer: Organizer, user: User, device: int, token: int, if user: locale = user.locale timezone = user.timezone + region = None # todo: add to user? else: e = allowed_events.first() if e: locale = e.settings.locale timezone = e.settings.timezone + region = e.settings.region else: locale = settings.LANGUAGE_CODE timezone = settings.TIME_ZONE - with language(locale), override(timezone): + region = None + with language(locale, region), override(timezone): if isinstance(form_data['events'][0], str): events = allowed_events.filter(slug__in=form_data.get('events'), organizer=organizer) else: diff --git a/src/pretix/base/services/invoices.py b/src/pretix/base/services/invoices.py index 69c8ec4bb..5dba47cfa 100644 --- a/src/pretix/base/services/invoices.py +++ b/src/pretix/base/services/invoices.py @@ -43,7 +43,7 @@ def build_invoice(invoice: Invoice) -> Invoice: lp = invoice.order.payments.last() - with language(invoice.locale): + with language(invoice.locale, invoice.event.settings.region): invoice.invoice_from = invoice.event.settings.get('invoice_address_from') invoice.invoice_from_name = invoice.event.settings.get('invoice_address_from_name') invoice.invoice_from_zipcode = invoice.event.settings.get('invoice_address_from_zipcode') @@ -244,7 +244,7 @@ def generate_cancellation(invoice: Invoice, trigger_pdf=True): cancellation.date = timezone.now().date() cancellation.payment_provider_text = '' cancellation.file = None - with language(invoice.locale): + with language(invoice.locale, invoice.event.settings.region): cancellation.invoice_from = invoice.event.settings.get('invoice_address_from') cancellation.invoice_from_name = invoice.event.settings.get('invoice_address_from_name') cancellation.invoice_from_zipcode = invoice.event.settings.get('invoice_address_from_zipcode') @@ -297,7 +297,7 @@ def invoice_pdf_task(invoice: int): return None if i.file: i.file.delete() - with language(i.locale): + with language(i.locale, i.event.settings.region): fname, ftype, fcontent = i.event.invoice_renderer.generate(i) i.file.save(fname, ContentFile(fcontent)) i.save() @@ -328,7 +328,7 @@ def build_preview_invoice_pdf(event): if not locale or locale == '__user__': locale = event.settings.locale - with rolledback_transaction(), language(locale): + with rolledback_transaction(), language(locale, event.settings.region): order = event.orders.create(status=Order.STATUS_PENDING, datetime=timezone.now(), expires=timezone.now(), code="PREVIEW", total=100 * event.tax_rules.count()) invoice = Invoice( diff --git a/src/pretix/base/services/mail.py b/src/pretix/base/services/mail.py index d547bb7af..ce773adf8 100644 --- a/src/pretix/base/services/mail.py +++ b/src/pretix/base/services/mail.py @@ -290,7 +290,7 @@ def mail_send_task(self, *args, to: List[str], subject: str, body: str, html: st except Order.DoesNotExist: order = None else: - with language(order.locale): + with language(order.locale, event.settings.region): if position: try: position = order.positions.get(pk=position) diff --git a/src/pretix/base/services/orderimport.py b/src/pretix/base/services/orderimport.py index ed463d2b3..605fc4655 100644 --- a/src/pretix/base/services/orderimport.py +++ b/src/pretix/base/services/orderimport.py @@ -65,7 +65,7 @@ def import_orders(event: Event, fileid: str, settings: dict, locale: str, user) # TODO: quotacheck? cf = CachedFile.objects.get(id=fileid) user = User.objects.get(pk=user) - with language(locale): + with language(locale, event.settings.region): cols = get_all_columns(event) parsed = parse_csv(cf.file) orders = [] @@ -163,7 +163,7 @@ def import_orders(event: Event, fileid: str, settings: dict, locale: str, user) ) for o in orders: - with language(o.locale): + with language(o.locale, event.settings.region): order_placed.send(event, order=o) if o.status == Order.STATUS_PAID: order_paid.send(event, order=o) diff --git a/src/pretix/base/services/orders.py b/src/pretix/base/services/orders.py index eea6d4745..a498bb649 100644 --- a/src/pretix/base/services/orders.py +++ b/src/pretix/base/services/orders.py @@ -23,7 +23,9 @@ from django_scopes import scopes_disabled from pretix.api.models import OAuthApplication from pretix.base.channels import get_all_sales_channels from pretix.base.email import get_email_context -from pretix.base.i18n import LazyLocaleException, language +from pretix.base.i18n import ( + LazyLocaleException, get_language_without_region, language, +) from pretix.base.models import ( CartPosition, Device, Event, GiftCard, Item, ItemVariation, Order, OrderPayment, OrderPosition, Quota, Seat, SeatCategoryMapping, User, @@ -260,7 +262,7 @@ def approve_order(order, user=None, send_mail: bool=True, auth=None, force=False # send_mail will trigger PDF generation later if send_mail: - with language(order.locale): + with language(order.locale, order.event.settings.region): if order.total == Decimal('0.00'): email_template = order.event.settings.mail_text_order_approved_free email_subject = _('Order approved and confirmed: %(code)s') % {'code': order.code} @@ -311,7 +313,7 @@ def deny_order(order, comment='', user=None, send_mail: bool=True, auth=None): if send_mail: email_template = order.event.settings.mail_text_order_denied email_context = get_email_context(event=order.event, order=order, comment=comment) - with language(order.locale): + with language(order.locale, order.event.settings.region): email_subject = _('Order denied: %(code)s') % {'code': order.code} try: order.send_mail( @@ -422,7 +424,7 @@ def _cancel_order(order, user=None, send_mail: bool=True, api_token=None, device if send_mail: email_template = order.event.settings.mail_text_order_canceled - with language(order.locale): + with language(order.locale, order.event.settings.region): email_context = get_email_context(event=order.event, order=order) email_subject = _('Order canceled: %(code)s') % {'code': order.code} try: @@ -777,7 +779,7 @@ def _create_order(event: Event, email: str, positions: List[CartPosition], now_d event=event, email=email, datetime=now_dt, - locale=locale, + locale=get_language_without_region(locale), total=total, testmode=True if sales_channel.testmode_supported and event.testmode else False, meta_info=json.dumps(meta_info or {}), @@ -1033,7 +1035,7 @@ def send_expiry_warnings(sender, **kwargs): # Race condition continue - with language(o.locale): + with language(o.locale, settings.region): o.expiry_reminder_sent = True o.save(update_fields=['expiry_reminder_sent']) email_template = settings.mail_text_order_expire_warning @@ -1110,7 +1112,7 @@ def send_download_reminders(sender, **kwargs): if not send: continue - with language(o.locale): + with language(o.locale, o.event.settings.region): o.download_reminder_sent = True o.save(update_fields=['download_reminder_sent']) email_template = event.settings.mail_text_download_reminder @@ -1150,7 +1152,7 @@ def send_download_reminders(sender, **kwargs): def notify_user_changed_order(order, user=None, auth=None, invoices=[]): - with language(order.locale): + with language(order.locale, order.event.settings.region): email_template = order.event.settings.mail_text_order_changed email_context = get_email_context(event=order.event, order=order) email_subject = _('Your order has been changed: %(code)s') % {'code': order.code} diff --git a/src/pretix/base/services/tickets.py b/src/pretix/base/services/tickets.py index facb0ee82..60b3f522f 100644 --- a/src/pretix/base/services/tickets.py +++ b/src/pretix/base/services/tickets.py @@ -23,7 +23,7 @@ logger = logging.getLogger(__name__) def generate_orderposition(order_position: int, provider: str): order_position = OrderPosition.objects.select_related('order', 'order__event').get(id=order_position) - with language(order_position.order.locale): + with language(order_position.order.locale, order_position.order.event.settings.region): responses = register_ticket_outputs.send(order_position.order.event) for receiver, response in responses: prov = response(order_position.order.event) @@ -41,7 +41,7 @@ def generate_orderposition(order_position: int, provider: str): def generate_order(order: int, provider: str): order = Order.objects.select_related('event').get(id=order) - with language(order.locale): + with language(order.locale, order.event.settings.region): responses = register_ticket_outputs.send(order.event) for receiver, response in responses: prov = response(order.event) @@ -75,7 +75,7 @@ class DummyRollbackException(Exception): def preview(event: int, provider: str): event = Event.objects.get(id=event) - with rolledback_transaction(), language(event.settings.locale): + with rolledback_transaction(), language(event.settings.locale, event.settings.region): item = event.items.create(name=_("Sample product"), default_price=42.23, description=_("Sample product description")) item2 = event.items.create(name=_("Sample workshop"), default_price=23.40) diff --git a/src/pretix/base/settings.py b/src/pretix/base/settings.py index fb5224923..912fd8eaf 100644 --- a/src/pretix/base/settings.py +++ b/src/pretix/base/settings.py @@ -832,6 +832,20 @@ DEFAULTS = { label=_("Default language"), ) }, + 'region': { + 'default': None, + 'type': str, + 'form_class': forms.ChoiceField, + 'serializer_class': serializers.ChoiceField, + 'serializer_kwargs': lambda: dict(**country_choice_kwargs()), + 'form_kwargs': lambda: dict( + label=_('Region'), + help_text=_('Will be used to determine date and time formatting as well as default country for customer ' + 'addresses and phone numbers. For formatting, this takes less priority than the language and ' + 'is therefore mostly relevant for languages used in different regions globally (like English).'), + **country_choice_kwargs() + ), + }, 'show_dates_on_frontpage': { 'default': 'True', 'type': bool, @@ -2403,3 +2417,9 @@ def validate_organizer_settings(organizer, settings_dict): # # N.B.: When actually fleshing out this stub, adding it to the OrganizerUpdateForm should be considered. pass + + +def global_settings_object(holder): + if not hasattr(holder, '_global_settings_object'): + holder._global_settings_object = GlobalSettingsObject() + return holder._global_settings_object diff --git a/src/pretix/control/forms/event.py b/src/pretix/control/forms/event.py index c630cdfe7..9eebca196 100644 --- a/src/pretix/control/forms/event.py +++ b/src/pretix/control/forms/event.py @@ -19,9 +19,7 @@ from pytz import common_timezones, timezone 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 import I18nModelForm, PlaceholderValidator, SettingsForm from pretix.base.models import Event, Organizer, TaxRule, Team from pretix.base.models.event import EventMetaValue, SubEvent from pretix.base.reldate import RelativeDateField, RelativeDateTimeField @@ -459,6 +457,7 @@ class EventSettingsForm(SettingsForm): 'presale_start_show_date', 'locales', 'locale', + 'region', 'show_quota_left', 'waiting_list_enabled', 'waiting_list_hours', diff --git a/src/pretix/control/forms/global_settings.py b/src/pretix/control/forms/global_settings.py index 4dfdc684d..325385552 100644 --- a/src/pretix/control/forms/global_settings.py +++ b/src/pretix/control/forms/global_settings.py @@ -10,11 +10,15 @@ from pretix.base.signals import register_global_settings class GlobalSettingsForm(SettingsForm): + auto_fields = [ + 'region' + ] + def __init__(self, *args, **kwargs): self.obj = GlobalSettingsObject() super().__init__(*args, obj=self.obj, **kwargs) - self.fields = OrderedDict([ + self.fields = OrderedDict(list(self.fields.items()) + [ ('footer_text', I18nFormField( widget=I18nTextInput, required=False, diff --git a/src/pretix/control/forms/organizer.py b/src/pretix/control/forms/organizer.py index 1fb80ee86..7aa161fa8 100644 --- a/src/pretix/control/forms/organizer.py +++ b/src/pretix/control/forms/organizer.py @@ -223,6 +223,7 @@ class OrganizerSettingsForm(SettingsForm): 'giftcard_length', 'giftcard_expiry_years', 'locales', + 'region', 'event_team_provisioning', 'primary_color', 'theme_color_success', diff --git a/src/pretix/control/templates/pretixcontrol/event/settings.html b/src/pretix/control/templates/pretixcontrol/event/settings.html index 5186ed98b..72eb5f63c 100644 --- a/src/pretix/control/templates/pretixcontrol/event/settings.html +++ b/src/pretix/control/templates/pretixcontrol/event/settings.html @@ -83,6 +83,7 @@ {% bootstrap_field sform.locales layout="control" %} {% bootstrap_field sform.locale layout="control" %} {% bootstrap_field sform.timezone layout="control" %} + {% bootstrap_field sform.region layout="control" %}