forked from CGM_Public/pretix_original
Compare commits
1 Commits
release/3.
...
docker-ngi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37f48ffd49 |
@@ -1,10 +1,3 @@
|
||||
doc/
|
||||
env/
|
||||
res/
|
||||
local/
|
||||
.git/
|
||||
pretixeu/
|
||||
src/data/
|
||||
src/pretix/static.dist/
|
||||
src/dist/
|
||||
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
user www-data www-data;
|
||||
worker_processes auto;
|
||||
worker_processes 1;
|
||||
pid /var/run/nginx.pid;
|
||||
daemon off;
|
||||
worker_rlimit_nofile 262144;
|
||||
|
||||
events {
|
||||
worker_connections 16384;
|
||||
multi_accept on;
|
||||
use epoll;
|
||||
worker_connections 4096;
|
||||
}
|
||||
|
||||
http {
|
||||
@@ -27,8 +24,8 @@ http {
|
||||
default_type application/octet-stream;
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
|
||||
access_log /var/log/nginx/access.log private;
|
||||
error_log /var/log/nginx/error.log;
|
||||
access_log /dev/stdout private;
|
||||
error_log /dev/stderr;
|
||||
add_header Referrer-Policy same-origin;
|
||||
|
||||
gzip on;
|
||||
|
||||
@@ -3,10 +3,9 @@ cd /pretix/src
|
||||
export DJANGO_SETTINGS_MODULE=production_settings
|
||||
export DATA_DIR=/data/
|
||||
export HOME=/pretix
|
||||
export NUM_WORKERS=$((2 * $(nproc --all)))
|
||||
|
||||
AUTOMIGRATE=${AUTOMIGRATE:-yes}
|
||||
NUM_WORKERS_DEFAULT=$((2 * $(nproc --all)))
|
||||
export NUM_WORKERS=${NUM_WORKERS:-$NUM_WORKERS_DEFAULT}
|
||||
|
||||
if [ ! -d /data/logs ]; then
|
||||
mkdir /data/logs;
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
file=/tmp/supervisor.sock
|
||||
|
||||
[supervisord]
|
||||
logfile=/tmp/supervisord.log
|
||||
logfile_maxbytes=50MB
|
||||
logfile=/dev/stderr
|
||||
logfile_maxbytes=0
|
||||
logfile_backups=10
|
||||
loglevel=info
|
||||
pidfile=/tmp/supervisord.pid
|
||||
|
||||
@@ -5,3 +5,7 @@ autorestart=true
|
||||
priority=10
|
||||
stdout_events_enabled=true
|
||||
stderr_events_enabled=true
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
|
||||
@@ -4,3 +4,7 @@ autostart=true
|
||||
autorestart=true
|
||||
priority=5
|
||||
user=pretixuser
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
|
||||
@@ -5,3 +5,7 @@ autorestart=true
|
||||
priority=5
|
||||
user=pretixuser
|
||||
environment=HOME=/pretix
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
|
||||
@@ -105,12 +105,7 @@ Example::
|
||||
|
||||
``csp_log``
|
||||
Log violations of the Content Security Policy (CSP). Defaults to ``on``.
|
||||
|
||||
``csp_additional_header``
|
||||
Specifies a CSP header that will be **merged** with pretix's default header. For example, if you set this
|
||||
to ``script-src https://mycdn.com``, pretix will add ``https://mycdn.com`` as an **additional** allowed source
|
||||
to all CSP headers. Empty by default.
|
||||
|
||||
|
||||
``loglevel``
|
||||
Set console and file log level (``DEBUG``, ``INFO``, ``WARNING``, ``ERROR`` or ``CRITICAL``). Defaults to ``INFO``.
|
||||
|
||||
|
||||
@@ -295,9 +295,7 @@ on one machine after each upgrade manually, otherwise multiple containers might
|
||||
database schema at the same time.
|
||||
|
||||
To run only the ``pretix-web`` component of pretix as well as a nginx server serving static files, you
|
||||
can invoke the container with ``docker run … pretix/standalone:stable web`` (instead of ``all``). You
|
||||
can adjust the number of ``gunicorn`` processes with the ``NUM_WORKERS`` environment variable (defaults to
|
||||
two times the number of CPUs detected).
|
||||
can invoke the container with ``docker run … pretix/standalone:stable web`` (instead of ``all``).
|
||||
|
||||
To run only ``pretix-worker``, you can run ``docker run … pretix/standalone:stable taskworker``. You can
|
||||
also pass arguments to limit the worker to specific queues or to change the number of concurrent task
|
||||
|
||||
@@ -30,7 +30,6 @@ testmode boolean If ``true``, th
|
||||
test mode. Only orders in test mode can be deleted.
|
||||
secret string The secret contained in the link sent to the customer
|
||||
email string The customer email address
|
||||
phone string The customer phone number
|
||||
locale string The locale used for communication with this customer
|
||||
sales_channel string Channel this sale was created through, such as
|
||||
``"web"``.
|
||||
@@ -168,10 +167,6 @@ last_modified datetime Last modificati
|
||||
|
||||
The ``subevent_before`` query parameter has been added.
|
||||
|
||||
.. versionchanged:: 3.14
|
||||
|
||||
The ``phone`` attribute has been added.
|
||||
|
||||
|
||||
.. _order-position-resource:
|
||||
|
||||
@@ -377,7 +372,6 @@ List of all orders
|
||||
"secret": "k24fiuwvu8kxz3y1",
|
||||
"url": "https://test.pretix.eu/dummy/dummy/order/ABC12/k24fiuwvu8kxz3y1/",
|
||||
"email": "tester@example.org",
|
||||
"phone": "+491234567",
|
||||
"locale": "en",
|
||||
"sales_channel": "web",
|
||||
"datetime": "2017-12-01T10:00:00Z",
|
||||
@@ -545,7 +539,6 @@ Fetching individual orders
|
||||
"secret": "k24fiuwvu8kxz3y1",
|
||||
"url": "https://test.pretix.eu/dummy/dummy/order/ABC12/k24fiuwvu8kxz3y1/",
|
||||
"email": "tester@example.org",
|
||||
"phone": "+491234567",
|
||||
"locale": "en",
|
||||
"sales_channel": "web",
|
||||
"datetime": "2017-12-01T10:00:00Z",
|
||||
@@ -712,8 +705,6 @@ Updating order fields
|
||||
|
||||
* ``email``
|
||||
|
||||
* ``phone``
|
||||
|
||||
* ``checkin_attention``
|
||||
|
||||
* ``locale``
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "3.14.2"
|
||||
__version__ = "3.14.0.dev0"
|
||||
|
||||
@@ -574,7 +574,6 @@ class EventSettingsSerializer(serializers.Serializer):
|
||||
'presale_start_show_date',
|
||||
'locales',
|
||||
'locale',
|
||||
'region',
|
||||
'last_order_modification_date',
|
||||
'show_quota_left',
|
||||
'waiting_list_enabled',
|
||||
@@ -601,9 +600,6 @@ class EventSettingsSerializer(serializers.Serializer):
|
||||
'attendee_data_explanation_text',
|
||||
'confirm_texts',
|
||||
'order_email_asked_twice',
|
||||
'order_phone_asked',
|
||||
'order_phone_required',
|
||||
'checkout_phone_helptext',
|
||||
'payment_term_mode',
|
||||
'payment_term_days',
|
||||
'payment_term_weekdays',
|
||||
|
||||
@@ -180,7 +180,7 @@ class PdfDataSerializer(serializers.Field):
|
||||
res = {}
|
||||
|
||||
ev = instance.subevent or instance.order.event
|
||||
with language(instance.order.locale, instance.order.event.settings.region):
|
||||
with language(instance.order.locale):
|
||||
# This needs to have some extra performance improvements to avoid creating hundreds of queries when
|
||||
# we serialize a list.
|
||||
|
||||
@@ -361,7 +361,7 @@ class OrderSerializer(I18nAwareModelSerializer):
|
||||
class Meta:
|
||||
model = Order
|
||||
fields = (
|
||||
'code', 'status', 'testmode', 'secret', 'email', 'phone', 'locale', 'datetime', 'expires', 'payment_date',
|
||||
'code', 'status', 'testmode', 'secret', 'email', 'locale', 'datetime', 'expires', 'payment_date',
|
||||
'payment_provider', 'fees', 'total', 'comment', 'invoice_address', 'positions', 'downloads',
|
||||
'checkin_attention', 'last_modified', 'payments', 'refunds', 'require_approval', 'sales_channel',
|
||||
'url'
|
||||
@@ -393,7 +393,7 @@ class OrderSerializer(I18nAwareModelSerializer):
|
||||
def update(self, instance, validated_data):
|
||||
# Even though all fields that shouldn't be edited are marked as read_only in the serializer
|
||||
# (hopefully), we'll be extra careful here and be explicit about the model fields we update.
|
||||
update_fields = ['comment', 'checkin_attention', 'email', 'locale', 'phone']
|
||||
update_fields = ['comment', 'checkin_attention', 'email', 'locale']
|
||||
|
||||
if 'invoice_address' in validated_data:
|
||||
iadata = validated_data.pop('invoice_address')
|
||||
@@ -691,7 +691,7 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = Order
|
||||
fields = ('code', 'status', 'testmode', 'email', 'phone', 'locale', 'payment_provider', 'fees', 'comment', 'sales_channel',
|
||||
fields = ('code', 'status', 'testmode', 'email', 'locale', 'payment_provider', 'fees', 'comment', 'sales_channel',
|
||||
'invoice_address', 'positions', 'checkin_attention', 'payment_info', 'payment_date', 'consume_carts',
|
||||
'force', 'send_email', 'simulate')
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from decimal import Decimal
|
||||
|
||||
from django.db.models import Q
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.translation import get_language, gettext_lazy as _
|
||||
from hierarkey.proxy import HierarkeyProxy
|
||||
from rest_framework import serializers
|
||||
from rest_framework.exceptions import ValidationError
|
||||
@@ -9,7 +9,6 @@ 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,
|
||||
@@ -146,7 +145,7 @@ class TeamInviteSerializer(serializers.ModelSerializer):
|
||||
})
|
||||
},
|
||||
event=None,
|
||||
locale=get_language_without_region() # TODO: expose?
|
||||
locale=get_language() # TODO: expose?
|
||||
)
|
||||
except SendMailException:
|
||||
pass # Already logged
|
||||
@@ -218,7 +217,6 @@ class OrganizerSettingsSerializer(serializers.Serializer):
|
||||
'giftcard_length',
|
||||
'giftcard_expiry_years',
|
||||
'locales',
|
||||
'region',
|
||||
'event_team_provisioning',
|
||||
'primary_color',
|
||||
'theme_color_success',
|
||||
|
||||
@@ -392,6 +392,6 @@ class EventSettingsView(views.APIView):
|
||||
}
|
||||
)
|
||||
if any(p in s.changed_data for p in SETTINGS_AFFECTING_CSS):
|
||||
regenerate_css.apply_async(args=(request.event.pk,))
|
||||
regenerate_css.apply_async(args=(request.organizer.pk,))
|
||||
s = EventSettingsSerializer(instance=request.event.settings, event=request.event)
|
||||
return Response(s.data)
|
||||
|
||||
@@ -81,9 +81,9 @@ class ExportersMixin:
|
||||
serializer = JobRunSerializer(exporter=instance, data=self.request.data, **self.get_serializer_kwargs())
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
cf = CachedFile(web_download=False)
|
||||
cf = CachedFile()
|
||||
cf.date = now()
|
||||
cf.expires = now() + timedelta(hours=24)
|
||||
cf.expires = now() + timedelta(days=3)
|
||||
cf.save()
|
||||
d = serializer.data
|
||||
for k, v in d.items():
|
||||
|
||||
@@ -582,7 +582,7 @@ class OrderViewSet(viewsets.ModelViewSet):
|
||||
auth=request.auth,
|
||||
)
|
||||
|
||||
with language(order.locale, self.request.event.settings.region):
|
||||
with language(order.locale):
|
||||
order_placed.send(self.request.event, order=order)
|
||||
if order.status == Order.STATUS_PAID:
|
||||
order_paid.send(self.request.event, order=order)
|
||||
@@ -674,17 +674,6 @@ class OrderViewSet(viewsets.ModelViewSet):
|
||||
}
|
||||
)
|
||||
|
||||
if 'phone' in self.request.data and serializer.instance.phone != self.request.data.get('phone'):
|
||||
serializer.instance.log_action(
|
||||
'pretix.event.order.phone.changed',
|
||||
user=self.request.user,
|
||||
auth=self.request.auth,
|
||||
data={
|
||||
'old_phone': serializer.instance.phone,
|
||||
'new_phone': self.request.data.get('phone'),
|
||||
}
|
||||
)
|
||||
|
||||
if 'locale' in self.request.data and serializer.instance.locale != self.request.data.get('locale'):
|
||||
serializer.instance.log_action(
|
||||
'pretix.event.order.locale.changed',
|
||||
@@ -897,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, self.request.event.settings.region):
|
||||
with language(data.get('locale') or self.request.event.settings.locale):
|
||||
return Response({
|
||||
'gross': price.gross,
|
||||
'gross_formatted': money_filter(price.gross, self.request.event.currency, hide_currency=True),
|
||||
|
||||
@@ -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 or get_language().split('-')[0] in settings.LANGUAGES_RTL,
|
||||
'rtl': get_language() in settings.LANGUAGES_RTL
|
||||
}
|
||||
if self.event:
|
||||
htmlctx['event'] = self.event
|
||||
|
||||
@@ -139,7 +139,7 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
tax_rates = self._get_all_tax_rates(qs)
|
||||
|
||||
headers = [
|
||||
_('Event slug'), _('Order code'), _('Order total'), _('Status'), _('Email'), _('Phone number'), _('Order date'),
|
||||
_('Event slug'), _('Order code'), _('Order total'), _('Status'), _('Email'), _('Order date'),
|
||||
_('Order time'), _('Company'), _('Name'),
|
||||
]
|
||||
name_scheme = PERSON_NAME_SCHEMES[self.event.settings.name_scheme] if not self.is_multievent else None
|
||||
@@ -215,7 +215,6 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
order.total,
|
||||
order.get_status_display(),
|
||||
order.email,
|
||||
str(order.phone) if order.phone else '',
|
||||
order.datetime.astimezone(tz).strftime('%Y-%m-%d'),
|
||||
order.datetime.astimezone(tz).strftime('%H:%M:%S'),
|
||||
]
|
||||
@@ -304,7 +303,6 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
_('Order code'),
|
||||
_('Status'),
|
||||
_('Email'),
|
||||
_('Phone number'),
|
||||
_('Order date'),
|
||||
_('Order time'),
|
||||
_('Fee type'),
|
||||
@@ -336,7 +334,6 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
order.code,
|
||||
order.get_status_display(),
|
||||
order.email,
|
||||
str(order.phone) if order.phone else '',
|
||||
order.datetime.astimezone(tz).strftime('%Y-%m-%d'),
|
||||
order.datetime.astimezone(tz).strftime('%H:%M:%S'),
|
||||
op.get_fee_type_display(),
|
||||
@@ -405,7 +402,6 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
_('Position ID'),
|
||||
_('Status'),
|
||||
_('Email'),
|
||||
_('Phone number'),
|
||||
_('Order date'),
|
||||
_('Order time'),
|
||||
]
|
||||
@@ -485,7 +481,6 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
op.positionid,
|
||||
order.get_status_display(),
|
||||
order.email,
|
||||
str(order.phone) if order.phone else '',
|
||||
order.datetime.astimezone(tz).strftime('%Y-%m-%d'),
|
||||
order.datetime.astimezone(tz).strftime('%H:%M:%S'),
|
||||
]
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
import hashlib
|
||||
import ipaddress
|
||||
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.password_validation import (
|
||||
password_validators_help_texts, validate_password,
|
||||
)
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from pretix.base.models import User
|
||||
from pretix.helpers.dicts import move_to_end
|
||||
from pretix.helpers.http import get_client_ip
|
||||
|
||||
|
||||
class LoginForm(forms.Form):
|
||||
@@ -23,7 +18,6 @@ class LoginForm(forms.Form):
|
||||
|
||||
error_messages = {
|
||||
'invalid_login': _("This combination of credentials is not known to our system."),
|
||||
'rate_limit': _("For security reasons, please wait 5 minutes before you try again."),
|
||||
'inactive': _("This account is inactive.")
|
||||
}
|
||||
|
||||
@@ -45,36 +39,10 @@ class LoginForm(forms.Form):
|
||||
else:
|
||||
move_to_end(self.fields, 'keep_logged_in')
|
||||
|
||||
@cached_property
|
||||
def ratelimit_key(self):
|
||||
if not settings.HAS_REDIS:
|
||||
return None
|
||||
client_ip = get_client_ip(self.request)
|
||||
if not client_ip:
|
||||
return None
|
||||
try:
|
||||
client_ip = ipaddress.ip_address(client_ip)
|
||||
except ValueError:
|
||||
# Web server not set up correctly
|
||||
return None
|
||||
if client_ip.is_private:
|
||||
# This is the private IP of the server, web server not set up correctly
|
||||
return None
|
||||
return 'pretix_login_{}'.format(hashlib.sha1(str(client_ip).encode()).hexdigest())
|
||||
|
||||
def clean(self):
|
||||
if all(k in self.cleaned_data for k, f in self.fields.items() if f.required):
|
||||
if self.ratelimit_key:
|
||||
from django_redis import get_redis_connection
|
||||
rc = get_redis_connection("redis")
|
||||
cnt = rc.get(self.ratelimit_key)
|
||||
if cnt and int(cnt) > 10:
|
||||
raise forms.ValidationError(self.error_messages['rate_limit'], code='rate_limit')
|
||||
self.user_cache = self.backend.form_authenticate(self.request, self.cleaned_data)
|
||||
if self.user_cache is None:
|
||||
if self.ratelimit_key:
|
||||
rc.incr(self.ratelimit_key)
|
||||
rc.expire(self.ratelimit_key, 300)
|
||||
raise forms.ValidationError(
|
||||
self.error_messages['invalid_login'],
|
||||
code='invalid_login'
|
||||
|
||||
@@ -9,34 +9,33 @@ import pycountry
|
||||
import pytz
|
||||
import vat_moss.errors
|
||||
import vat_moss.id
|
||||
from babel import Locale
|
||||
from babel import localedata
|
||||
from django import forms
|
||||
from django.contrib import messages
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||
from django.db.models import QuerySet
|
||||
from django.forms import Select
|
||||
from django.utils import translation
|
||||
from django.utils.formats import date_format
|
||||
from django.utils.html import escape
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.timezone import get_current_timezone
|
||||
from django.utils.translation import gettext_lazy as _, pgettext_lazy
|
||||
from django.utils.translation import (
|
||||
get_language, gettext_lazy as _, pgettext_lazy,
|
||||
)
|
||||
from django_countries import countries
|
||||
from django_countries.fields import Country, CountryField
|
||||
from phonenumber_field.formfields import PhoneNumberField
|
||||
from phonenumber_field.phonenumber import PhoneNumber
|
||||
from phonenumber_field.widgets import PhoneNumberPrefixWidget
|
||||
from phonenumbers import NumberParseException, national_significant_number
|
||||
from phonenumbers import NumberParseException
|
||||
from phonenumbers.data import _COUNTRY_CODE_TO_REGION_CODE
|
||||
|
||||
from pretix.base.forms.widgets import (
|
||||
BusinessBooleanRadio, DatePickerWidget, SplitDateTimePickerWidget,
|
||||
TimePickerWidget, UploadedFileWidget,
|
||||
)
|
||||
from pretix.base.i18n import (
|
||||
get_babel_locale, get_language_without_region, language,
|
||||
)
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.models import InvoiceAddress, Question, QuestionOption
|
||||
from pretix.base.models.tax import (
|
||||
EU_COUNTRIES, cc_to_vat_prefix, is_eu_country,
|
||||
@@ -205,47 +204,7 @@ class NamePartsFormField(forms.MultiValueField):
|
||||
return value
|
||||
|
||||
|
||||
class WrappedPhonePrefixSelect(Select):
|
||||
initial = None
|
||||
|
||||
def __init__(self, initial=None):
|
||||
choices = [("", "---------")]
|
||||
language = get_babel_locale() # changed from default implementation that used the django locale
|
||||
locale = Locale(translation.to_locale(language))
|
||||
for prefix, values in _COUNTRY_CODE_TO_REGION_CODE.items():
|
||||
prefix = "+%d" % prefix
|
||||
if initial and initial in values:
|
||||
self.initial = prefix
|
||||
for country_code in values:
|
||||
country_name = locale.territories.get(country_code)
|
||||
if country_name:
|
||||
choices.append((prefix, "{} {}".format(country_name, prefix)))
|
||||
super().__init__(choices=sorted(choices, key=lambda item: item[1]))
|
||||
|
||||
def render(self, name, value, *args, **kwargs):
|
||||
return super().render(name, value or self.initial, *args, **kwargs)
|
||||
|
||||
def get_context(self, name, value, attrs):
|
||||
if value and self.choices[1][0] != value:
|
||||
matching_choices = len([1 for p, c in self.choices if p == value])
|
||||
if matching_choices > 1:
|
||||
# Some countries share a phone pretix, for example +1 is used all over the Americas.
|
||||
# This causes a UX problem: If the default value or the existing data is +12125552368,
|
||||
# the widget will just show the first <option> entry with value="+1" as selected,
|
||||
# which alphabetically is America Samoa, although most numbers statistically are from
|
||||
# the US. As a workaround, we detect this case and add an aditional choice value with
|
||||
# just <option value="+1">+1</option> without an explicit country.
|
||||
self.choices.insert(1, (value, value))
|
||||
context = super().get_context(name, value, attrs)
|
||||
return context
|
||||
|
||||
|
||||
class WrappedPhoneNumberPrefixWidget(PhoneNumberPrefixWidget):
|
||||
|
||||
def __init__(self, attrs=None, initial=None):
|
||||
widgets = (WrappedPhonePrefixSelect(initial), forms.TextInput())
|
||||
super(PhoneNumberPrefixWidget, self).__init__(widgets, attrs)
|
||||
|
||||
def render(self, name, value, attrs=None, renderer=None):
|
||||
output = super().render(name, value, attrs, renderer)
|
||||
return mark_safe(self.format_output(output))
|
||||
@@ -253,44 +212,12 @@ class WrappedPhoneNumberPrefixWidget(PhoneNumberPrefixWidget):
|
||||
def format_output(self, rendered_widgets) -> str:
|
||||
return '<div class="nameparts-form-group">%s</div>' % ''.join(rendered_widgets)
|
||||
|
||||
def decompress(self, value):
|
||||
"""
|
||||
If an incomplete phone number (e.g. without country prefix) is currently entered,
|
||||
the default implementation just discards the value and shows nothing at all.
|
||||
Let's rather show something invalid, so the user is prompted to fix it, instead of
|
||||
silently deleting data.
|
||||
"""
|
||||
if value:
|
||||
if type(value) == PhoneNumber:
|
||||
if value.country_code and value.national_number:
|
||||
return [
|
||||
"+%d" % value.country_code,
|
||||
national_significant_number(value),
|
||||
]
|
||||
return [
|
||||
None,
|
||||
str(value)
|
||||
]
|
||||
elif "." in value:
|
||||
return value.split(".")
|
||||
else:
|
||||
return [None, value]
|
||||
return [None, ""]
|
||||
|
||||
def value_from_datadict(self, data, files, name):
|
||||
# In contrast to defualt implementation, do not silently fail if a number without
|
||||
# country prefix is entered
|
||||
values = super(PhoneNumberPrefixWidget, self).value_from_datadict(data, files, name)
|
||||
if values[1]:
|
||||
return "%s.%s" % tuple(values)
|
||||
return ""
|
||||
|
||||
|
||||
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_without_region()
|
||||
country = event.settings.region or event.settings.invoice_address_from_country
|
||||
locale = get_language()
|
||||
country = event.settings.invoice_address_from_country
|
||||
if not country:
|
||||
valid_countries = countries.countries
|
||||
if '-' in locale:
|
||||
@@ -605,7 +532,13 @@ class BaseQuestionsForm(forms.Form):
|
||||
if q.valid_datetime_max:
|
||||
field.validators.append(MaxDateTimeValidator(q.valid_datetime_max))
|
||||
elif q.type == Question.TYPE_PHONENUMBER:
|
||||
with language(get_babel_locale()):
|
||||
babel_locale = 'en'
|
||||
# Babel, and therefore django-phonenumberfield, do not support our custom locales such das de_Informal
|
||||
if localedata.exists(get_language()):
|
||||
babel_locale = get_language()
|
||||
elif localedata.exists(get_language()[:2]):
|
||||
babel_locale = get_language()[:2]
|
||||
with language(babel_locale):
|
||||
default_country = guess_country(event)
|
||||
default_prefix = None
|
||||
for prefix, values in _COUNTRY_CODE_TO_REGION_CODE.items():
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.hashers import check_password
|
||||
from django.contrib.auth.password_validation import (
|
||||
password_validators_help_texts, validate_password,
|
||||
@@ -20,7 +19,6 @@ class UserSettingsForm(forms.ModelForm):
|
||||
"address or password."),
|
||||
'pw_current_wrong': _("The current password you entered was not correct."),
|
||||
'pw_mismatch': _("Please enter the same password twice"),
|
||||
'rate_limit': _("For security reasons, please wait 5 minutes before you try again."),
|
||||
}
|
||||
|
||||
old_pw = forms.CharField(max_length=255,
|
||||
@@ -66,18 +64,6 @@ class UserSettingsForm(forms.ModelForm):
|
||||
|
||||
def clean_old_pw(self):
|
||||
old_pw = self.cleaned_data.get('old_pw')
|
||||
|
||||
if old_pw and settings.HAS_REDIS:
|
||||
from django_redis import get_redis_connection
|
||||
rc = get_redis_connection("redis")
|
||||
cnt = rc.incr('pretix_pwchange_%s' % self.user.pk)
|
||||
rc.expire('pretix_pwchange_%s' % self.user.pk, 300)
|
||||
if cnt > 10:
|
||||
raise forms.ValidationError(
|
||||
self.error_messages['rate_limit'],
|
||||
code='rate_limit',
|
||||
)
|
||||
|
||||
if old_pw and not check_password(old_pw, self.user.password):
|
||||
raise forms.ValidationError(
|
||||
self.error_messages['pw_current_wrong'],
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from contextlib import contextmanager
|
||||
|
||||
from babel import localedata
|
||||
from django.conf import settings
|
||||
from django.utils import translation
|
||||
from django.utils.formats import date_format, number_format
|
||||
@@ -67,52 +66,10 @@ class LazyNumber:
|
||||
return number_format(self.value, decimal_pos=self.decimal_pos)
|
||||
|
||||
|
||||
ALLOWED_LANGUAGES = dict(settings.LANGUAGES)
|
||||
|
||||
|
||||
def get_babel_locale():
|
||||
babel_locale = 'en'
|
||||
# Babel, and therefore django-phonenumberfield, do not support our custom locales such das de_Informal
|
||||
if localedata.exists(translation.get_language()):
|
||||
babel_locale = translation.get_language()
|
||||
elif localedata.exists(translation.get_language()[:2]):
|
||||
babel_locale = translation.get_language()[:2]
|
||||
return babel_locale
|
||||
|
||||
|
||||
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, 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.
|
||||
"""
|
||||
def language(lng):
|
||||
_lng = translation.get_language()
|
||||
lng = lng or settings.LANGUAGE_CODE
|
||||
if '-' not in lng and region:
|
||||
lng += '-' + region.lower()
|
||||
translation.activate(lng)
|
||||
translation.activate(lng or settings.LANGUAGE_CODE)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
|
||||
@@ -144,7 +144,7 @@ class BaseReportlabInvoiceRenderer(BaseInvoiceRenderer):
|
||||
|
||||
def _upper(self, val):
|
||||
# We uppercase labels, but not in every language
|
||||
if get_language().startswith('el'):
|
||||
if get_language() == 'el':
|
||||
return val
|
||||
return val.upper()
|
||||
|
||||
|
||||
@@ -15,8 +15,7 @@ from django.utils.translation.trans_real import (
|
||||
parse_accept_lang_header,
|
||||
)
|
||||
|
||||
from pretix.base.i18n import get_language_without_region
|
||||
from pretix.base.settings import global_settings_object
|
||||
from pretix.base.settings import GlobalSettingsObject
|
||||
from pretix.multidomain.urlreverse import (
|
||||
get_event_domain, get_organizer_domain,
|
||||
)
|
||||
@@ -36,30 +35,19 @@ 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 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
|
||||
|
||||
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
|
||||
translation.activate(language)
|
||||
request.LANGUAGE_CODE = get_language_without_region()
|
||||
request.LANGUAGE_CODE = translation.get_language()
|
||||
|
||||
tzname = None
|
||||
if hasattr(request, 'event'):
|
||||
@@ -204,7 +192,7 @@ class SecurityMiddleware(MiddlewareMixin):
|
||||
resp['P3P'] = 'CP=\"ALL DSP COR CUR ADM TAI OUR IND COM NAV INT\"'
|
||||
|
||||
img_src = []
|
||||
gs = global_settings_object(request)
|
||||
gs = GlobalSettingsObject()
|
||||
if gs.settings.leaflet_tiles:
|
||||
img_src.append(gs.settings.leaflet_tiles[:gs.settings.leaflet_tiles.index("/", 10)].replace("{s}", "*"))
|
||||
|
||||
@@ -228,8 +216,6 @@ class SecurityMiddleware(MiddlewareMixin):
|
||||
h['report-uri'] = ["/csp_report/"]
|
||||
if 'Content-Security-Policy' in resp:
|
||||
_merge_csp(h, _parse_csp(resp['Content-Security-Policy']))
|
||||
if settings.CSP_ADDITIONAL_HEADER:
|
||||
_merge_csp(h, _parse_csp(settings.CSP_ADDITIONAL_HEADER))
|
||||
|
||||
staticdomain = "'self'"
|
||||
dynamicdomain = "'self'"
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
# Generated by Django 3.0.11 on 2020-12-18 18:10
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pretixbase', '0162_remove_seat_name'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='cachedfile',
|
||||
name='session_key',
|
||||
field=models.TextField(null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='cachedfile',
|
||||
name='web_download',
|
||||
field=models.BooleanField(default=True),
|
||||
),
|
||||
]
|
||||
@@ -1,51 +0,0 @@
|
||||
# Generated by Django 3.0.11 on 2020-12-11 16:48
|
||||
import json
|
||||
|
||||
import phonenumber_field.modelfields
|
||||
from django.db import migrations
|
||||
|
||||
import pretix.base.models.fields
|
||||
|
||||
|
||||
def migrate_settings(apps, schema_editor):
|
||||
Order = apps.get_model('pretixbase', 'Order')
|
||||
Event = apps.get_model('pretixbase', 'Event')
|
||||
Event_SettingsStore = apps.get_model('pretixbase', 'Event_SettingsStore')
|
||||
Event_SettingsStore.objects.filter(key='telephone_field_required').update(key='order_phone_required')
|
||||
Event_SettingsStore.objects.filter(key='telephone_field_help_text').update(key='checkout_phone_helptext')
|
||||
for e in Event.objects.filter(plugins__icontains="pretix_telephone"):
|
||||
plugins = e.plugins.split(",")
|
||||
plugins.remove("pretix_telephone")
|
||||
e.plugins = ",".join(plugins)
|
||||
e.save()
|
||||
Event_SettingsStore.objects.create(object=e, key='order_phone_asked', value='True')
|
||||
for o in Order.objects.filter(meta_info__icontains='"telephone"'):
|
||||
mi = json.loads(o.meta_info)
|
||||
if 'telephone' in mi.get('contact_form_data', {}):
|
||||
mi['phone'] = mi['contact_form_data'].pop('telephone')
|
||||
o.phone = mi['phone']
|
||||
o.meta_info = json.dumps(mi)
|
||||
o.save(update_fields=['meta_info', 'phone'])
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pretixbase', '0172_event_sales_channels'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='order',
|
||||
name='phone',
|
||||
field=phonenumber_field.modelfields.PhoneNumberField(max_length=128, null=True, region=None),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='sales_channels',
|
||||
field=pretix.base.models.fields.MultiStringField(default=['web']),
|
||||
),
|
||||
migrations.RunPython(
|
||||
migrate_settings, migrations.RunPython.noop,
|
||||
)
|
||||
]
|
||||
@@ -1,14 +0,0 @@
|
||||
# Generated by Django 3.0.11 on 2020-12-22 10:31
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pretixbase', '0173_auto_20201211_1648'),
|
||||
('pretixbase', '0162b_auto_20201218_1810'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
]
|
||||
@@ -28,8 +28,6 @@ class CachedFile(models.Model):
|
||||
filename = models.CharField(max_length=255)
|
||||
type = models.CharField(max_length=255)
|
||||
file = models.FileField(null=True, blank=True, upload_to=cachedfile_name, max_length=255)
|
||||
web_download = models.BooleanField(default=True) # allow web download, True for backwards compatibility in plugins
|
||||
session_key = models.TextField(null=True, blank=True) # only allow download in this session
|
||||
|
||||
|
||||
@receiver(post_delete, sender=CachedFile)
|
||||
|
||||
@@ -528,7 +528,7 @@ class Event(EventMixin, LoggedModel):
|
||||
|
||||
return locking.LockManager(self)
|
||||
|
||||
def get_mail_backend(self, timeout=None, force_custom=False):
|
||||
def get_mail_backend(self, force_custom=False):
|
||||
"""
|
||||
Returns an email server connection, either by using the system-wide connection
|
||||
or by returning a custom one based on the event's settings.
|
||||
@@ -542,7 +542,7 @@ class Event(EventMixin, LoggedModel):
|
||||
password=self.settings.smtp_password,
|
||||
use_tls=self.settings.smtp_use_tls,
|
||||
use_ssl=self.settings.smtp_use_ssl,
|
||||
fail_silently=False, timeout=timeout)
|
||||
fail_silently=False)
|
||||
else:
|
||||
return get_connection(fail_silently=False)
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ from django_countries.fields import Country
|
||||
from django_scopes import ScopedManager, scopes_disabled
|
||||
from i18nfield.strings import LazyI18nString
|
||||
from jsonfallback.fields import FallbackJSONField
|
||||
from phonenumber_field.modelfields import PhoneNumberField
|
||||
from phonenumber_field.phonenumber import PhoneNumber
|
||||
from phonenumbers import NumberParseException
|
||||
|
||||
@@ -87,8 +86,6 @@ class Order(LockModel, LoggedModel):
|
||||
:type event: Event
|
||||
:param email: The email of the person who ordered this
|
||||
:type email: str
|
||||
:param phone: The phone number of the person who ordered this
|
||||
:type phone: str
|
||||
:param testmode: Whether this is a test mode order
|
||||
:type testmode: bool
|
||||
:param locale: The locale of this order
|
||||
@@ -147,10 +144,6 @@ class Order(LockModel, LoggedModel):
|
||||
null=True, blank=True,
|
||||
verbose_name=_('E-mail')
|
||||
)
|
||||
phone = PhoneNumberField(
|
||||
null=True, blank=True,
|
||||
verbose_name=_('Phone number'),
|
||||
)
|
||||
locale = models.CharField(
|
||||
null=True, blank=True, max_length=32,
|
||||
verbose_name=_('Locale')
|
||||
@@ -333,9 +326,6 @@ class Order(LockModel, LoggedModel):
|
||||
payment_sum=payment_sum_sq,
|
||||
refund_sum=refund_sum_sq,
|
||||
)
|
||||
qs = qs.annotate(
|
||||
computed_payment_refund_sum=Coalesce(payment_sum_sq, 0) - Coalesce(refund_sum_sq, 0),
|
||||
)
|
||||
|
||||
qs = qs.annotate(
|
||||
pending_sum_t=F('total') - Coalesce(payment_sum_sq, 0) + Coalesce(refund_sum_sq, 0),
|
||||
@@ -867,7 +857,7 @@ class Order(LockModel, LoggedModel):
|
||||
for k, v in self.event.meta_data.items():
|
||||
context['meta_' + k] = v
|
||||
|
||||
with language(self.locale, self.event.settings.region):
|
||||
with language(self.locale):
|
||||
recipient = self.email
|
||||
if position and position.attendee_email:
|
||||
recipient = position.attendee_email
|
||||
@@ -900,7 +890,7 @@ class Order(LockModel, LoggedModel):
|
||||
)
|
||||
|
||||
def resend_link(self, user=None, auth=None):
|
||||
with language(self.locale, self.event.settings.region):
|
||||
with language(self.locale):
|
||||
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}
|
||||
@@ -912,7 +902,7 @@ class Order(LockModel, LoggedModel):
|
||||
|
||||
@property
|
||||
def positions_with_tickets(self):
|
||||
for op in self.positions.select_related('item'):
|
||||
for op in self.positions.all():
|
||||
if not op.generate_ticket:
|
||||
continue
|
||||
yield op
|
||||
@@ -1165,7 +1155,7 @@ class AbstractPosition(models.Model):
|
||||
(2) questions: a list of Question objects, extended by an 'answer' property
|
||||
"""
|
||||
self.answ = {}
|
||||
for a in getattr(self, 'answerlist', self.answers.all()): # use prefetch_related cache from get_cart
|
||||
for a in self.answers.all():
|
||||
self.answ[a.question_id] = a
|
||||
|
||||
# We need to clone our question objects, otherwise we will override the cached
|
||||
@@ -1524,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, self.order.event.settings.region):
|
||||
with language(self.order.locale):
|
||||
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}
|
||||
@@ -1542,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, self.order.event.settings.region):
|
||||
with language(self.order.locale):
|
||||
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}
|
||||
@@ -2114,7 +2104,7 @@ class OrderPosition(AbstractPosition):
|
||||
for k, v in self.event.meta_data.items():
|
||||
context['meta_' + k] = v
|
||||
|
||||
with language(self.order.locale, self.order.event.settings.region):
|
||||
with language(self.order.locale):
|
||||
recipient = self.attendee_email
|
||||
try:
|
||||
email_content = render_mail(template, context)
|
||||
@@ -2142,7 +2132,7 @@ class OrderPosition(AbstractPosition):
|
||||
|
||||
def resend_link(self, user=None, auth=None):
|
||||
|
||||
with language(self.order.locale, self.order.event.settings.region):
|
||||
with language(self.order.locale):
|
||||
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}
|
||||
|
||||
@@ -125,7 +125,7 @@ class WaitingListEntry(LoggedModel):
|
||||
self.voucher = v
|
||||
self.save()
|
||||
|
||||
with language(self.locale, self.event.settings.region):
|
||||
with language(self.locale):
|
||||
mail(
|
||||
self.email,
|
||||
_('You have been selected from the waitinglist for {event}').format(event=str(self.event)),
|
||||
|
||||
@@ -39,7 +39,6 @@ from pretix.base.models import Order, OrderPosition
|
||||
from pretix.base.settings import PERSON_NAME_SCHEMES
|
||||
from pretix.base.signals import layout_text_variables
|
||||
from pretix.base.templatetags.money import money_filter
|
||||
from pretix.base.templatetags.phone_format import phone_format
|
||||
from pretix.presale.style import get_fonts
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -230,11 +229,6 @@ DEFAULT_VARIABLES = OrderedDict((
|
||||
"editor_sample": _("Random City"),
|
||||
"evaluate": lambda op, order, ev: str(ev.location)
|
||||
}),
|
||||
("telephone", {
|
||||
"label": _("Phone number"),
|
||||
"editor_sample": "+01 1234 567890",
|
||||
"evaluate": lambda op, order, ev: phone_format(order.phone)
|
||||
}),
|
||||
("invoice_name", {
|
||||
"label": _("Invoice address name"),
|
||||
"editor_sample": _("John Doe"),
|
||||
@@ -427,7 +421,6 @@ 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)
|
||||
@@ -494,7 +487,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'], self.event.settings.region):
|
||||
with language(o['locale']):
|
||||
return self._get_text_content(op, order, o, True)
|
||||
|
||||
ev = self._get_ev(op, order)
|
||||
|
||||
@@ -24,7 +24,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _send_wle_mail(wle: WaitingListEntry, subject: LazyI18nString, message: LazyI18nString, subevent: SubEvent):
|
||||
with language(wle.locale, wle.event.settings.region):
|
||||
with language(wle.locale):
|
||||
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, order.event.settings.region):
|
||||
with language(order.locale):
|
||||
try:
|
||||
ia = order.invoice_address
|
||||
except InvoiceAddress.DoesNotExist:
|
||||
|
||||
@@ -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, event.settings.region), override(event.settings.timezone):
|
||||
with language(event.settings.locale), override(event.settings.timezone):
|
||||
responses = register_data_exporters.send(event)
|
||||
for receiver, response in responses:
|
||||
ex = response(event, set_progress)
|
||||
@@ -67,18 +67,15 @@ 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
|
||||
region = None
|
||||
with language(locale, region), override(timezone):
|
||||
with language(locale), override(timezone):
|
||||
if isinstance(form_data['events'][0], str):
|
||||
events = allowed_events.filter(slug__in=form_data.get('events'), organizer=organizer)
|
||||
else:
|
||||
|
||||
@@ -43,7 +43,7 @@ def build_invoice(invoice: Invoice) -> Invoice:
|
||||
|
||||
lp = invoice.order.payments.last()
|
||||
|
||||
with language(invoice.locale, invoice.event.settings.region):
|
||||
with language(invoice.locale):
|
||||
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, invoice.event.settings.region):
|
||||
with language(invoice.locale):
|
||||
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, i.event.settings.region):
|
||||
with language(i.locale):
|
||||
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, event.settings.region):
|
||||
with rolledback_transaction(), language(locale):
|
||||
order = event.orders.create(status=Order.STATUS_PENDING, datetime=timezone.now(),
|
||||
expires=timezone.now(), code="PREVIEW", total=100 * event.tax_rules.count())
|
||||
invoice = Invoice(
|
||||
|
||||
@@ -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, event.settings.region):
|
||||
with language(order.locale):
|
||||
if position:
|
||||
try:
|
||||
position = order.positions.get(pk=position)
|
||||
|
||||
@@ -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, event.settings.region):
|
||||
with language(locale):
|
||||
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, event.settings.region):
|
||||
with language(o.locale):
|
||||
order_placed.send(event, order=o)
|
||||
if o.status == Order.STATUS_PAID:
|
||||
order_paid.send(event, order=o)
|
||||
|
||||
@@ -23,9 +23,7 @@ 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, get_language_without_region, language,
|
||||
)
|
||||
from pretix.base.i18n import LazyLocaleException, language
|
||||
from pretix.base.models import (
|
||||
CartPosition, Device, Event, GiftCard, Item, ItemVariation, Order,
|
||||
OrderPayment, OrderPosition, Quota, Seat, SeatCategoryMapping, User,
|
||||
@@ -262,7 +260,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, order.event.settings.region):
|
||||
with language(order.locale):
|
||||
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}
|
||||
@@ -313,7 +311,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, order.event.settings.region):
|
||||
with language(order.locale):
|
||||
email_subject = _('Order denied: %(code)s') % {'code': order.code}
|
||||
try:
|
||||
order.send_mail(
|
||||
@@ -424,7 +422,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, order.event.settings.region):
|
||||
with language(order.locale):
|
||||
email_context = get_email_context(event=order.event, order=order)
|
||||
email_subject = _('Order canceled: %(code)s') % {'code': order.code}
|
||||
try:
|
||||
@@ -778,9 +776,8 @@ def _create_order(event: Event, email: str, positions: List[CartPosition], now_d
|
||||
status=Order.STATUS_PENDING,
|
||||
event=event,
|
||||
email=email,
|
||||
phone=(meta_info or {}).get('contact_form_data', {}).get('phone'),
|
||||
datetime=now_dt,
|
||||
locale=get_language_without_region(locale),
|
||||
locale=locale,
|
||||
total=total,
|
||||
testmode=True if sales_channel.testmode_supported and event.testmode else False,
|
||||
meta_info=json.dumps(meta_info or {}),
|
||||
@@ -1036,7 +1033,7 @@ def send_expiry_warnings(sender, **kwargs):
|
||||
# Race condition
|
||||
continue
|
||||
|
||||
with language(o.locale, settings.region):
|
||||
with language(o.locale):
|
||||
o.expiry_reminder_sent = True
|
||||
o.save(update_fields=['expiry_reminder_sent'])
|
||||
email_template = settings.mail_text_order_expire_warning
|
||||
@@ -1113,7 +1110,7 @@ def send_download_reminders(sender, **kwargs):
|
||||
if not send:
|
||||
continue
|
||||
|
||||
with language(o.locale, o.event.settings.region):
|
||||
with language(o.locale):
|
||||
o.download_reminder_sent = True
|
||||
o.save(update_fields=['download_reminder_sent'])
|
||||
email_template = event.settings.mail_text_download_reminder
|
||||
@@ -1153,7 +1150,7 @@ def send_download_reminders(sender, **kwargs):
|
||||
|
||||
|
||||
def notify_user_changed_order(order, user=None, auth=None, invoices=[]):
|
||||
with language(order.locale, order.event.settings.region):
|
||||
with language(order.locale):
|
||||
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}
|
||||
|
||||
@@ -113,11 +113,10 @@ class QuotaAvailability:
|
||||
raise e
|
||||
|
||||
def _write_cache(self, quotas, now_dt):
|
||||
# We used to also delete item_quota_cache:* from the event cache here, but as the cache
|
||||
# gets more complex, this does not seem worth it. The cache is only present for up to
|
||||
# 5 seconds to prevent high peaks, and a 5-second delay in availability is usually
|
||||
# tolerable
|
||||
events = {q.event for q in quotas}
|
||||
update = []
|
||||
for e in events:
|
||||
e.cache.delete('item_quota_cache')
|
||||
for q in quotas:
|
||||
rewrite_cache = self._count_waitinglist and (
|
||||
not q.cache_is_hot(now_dt) or self.results[q][0] > q.cached_availability_state
|
||||
|
||||
@@ -17,7 +17,7 @@ from pretix.celery_app import app
|
||||
|
||||
|
||||
@app.task(base=ProfiledEventTask)
|
||||
def export(event: Event, shredders: List[str], session_key=None) -> None:
|
||||
def export(event: Event, shredders: List[str]) -> None:
|
||||
known_shredders = event.get_data_shredders()
|
||||
|
||||
with NamedTemporaryFile() as rawfile:
|
||||
@@ -55,8 +55,6 @@ def export(event: Event, shredders: List[str], session_key=None) -> None:
|
||||
cf.date = now()
|
||||
cf.filename = event.slug + '.zip'
|
||||
cf.type = 'application/zip'
|
||||
cf.session_key = session_key
|
||||
cf.web_download = True
|
||||
cf.expires = now() + timedelta(hours=1)
|
||||
cf.save()
|
||||
cf.file.save(cachedfile_name(cf, cf.filename), rawfile)
|
||||
|
||||
@@ -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, order_position.order.event.settings.region):
|
||||
with language(order_position.order.locale):
|
||||
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, order.event.settings.region):
|
||||
with language(order.locale):
|
||||
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, event.settings.region):
|
||||
with rolledback_transaction(), language(event.settings.locale):
|
||||
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)
|
||||
|
||||
@@ -193,25 +193,6 @@ DEFAULTS = {
|
||||
help_text=_("Require customers to fill in the primary email address twice to avoid errors."),
|
||||
)
|
||||
},
|
||||
'order_phone_asked': {
|
||||
'default': 'False',
|
||||
'type': bool,
|
||||
'form_class': forms.BooleanField,
|
||||
'serializer_class': serializers.BooleanField,
|
||||
'form_kwargs': dict(
|
||||
label=_("Ask for a phone number per order"),
|
||||
)
|
||||
},
|
||||
'order_phone_required': {
|
||||
'default': 'False',
|
||||
'type': bool,
|
||||
'form_class': forms.BooleanField,
|
||||
'serializer_class': serializers.BooleanField,
|
||||
'form_kwargs': dict(
|
||||
label=_("Require a phone number per order"),
|
||||
widget=forms.CheckboxInput(attrs={'data-checkbox-dependency': '#id_settings-order_phone_asked'}),
|
||||
)
|
||||
},
|
||||
'invoice_address_asked': {
|
||||
'default': 'True',
|
||||
'type': bool,
|
||||
@@ -851,20 +832,6 @@ 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,
|
||||
@@ -1878,17 +1845,6 @@ Your {event} team"""))
|
||||
"why you need information from them.")
|
||||
)
|
||||
},
|
||||
'checkout_phone_helptext': {
|
||||
'default': '',
|
||||
'type': LazyI18nString,
|
||||
'serializer_class': I18nField,
|
||||
'form_class': I18nFormField,
|
||||
'form_kwargs': dict(
|
||||
label=_("Help text of the phone number field"),
|
||||
widget_kwargs={'attrs': {'rows': '2'}},
|
||||
widget=I18nTextarea
|
||||
)
|
||||
},
|
||||
'checkout_email_helptext': {
|
||||
'default': LazyI18nString.from_gettext(gettext_noop(
|
||||
'Make sure to enter a valid email address. We will send you an order '
|
||||
@@ -2447,9 +2403,3 @@ 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
|
||||
|
||||
@@ -20,7 +20,6 @@ from pretix.base.models import (
|
||||
)
|
||||
from pretix.base.services.invoices import invoice_pdf_task
|
||||
from pretix.base.signals import register_data_shredders
|
||||
from pretix.helpers.json import CustomJSONEncoder
|
||||
|
||||
|
||||
class ShredError(LazyLocaleException):
|
||||
@@ -122,31 +121,6 @@ def shred_log_fields(logentry, banlist=None, whitelist=None):
|
||||
logentry.save(update_fields=['data', 'shredded'])
|
||||
|
||||
|
||||
class PhoneNumberShredder(BaseDataShredder):
|
||||
verbose_name = _('Phone numbers')
|
||||
identifier = 'phone_numbers'
|
||||
description = _('This will remove all phone numbers from orders.')
|
||||
|
||||
def generate_files(self) -> List[Tuple[str, str, str]]:
|
||||
yield 'phone-by-order.json', 'application/json', json.dumps({
|
||||
o.code: o.phone for o in self.event.orders.filter(phone__isnull=False)
|
||||
}, cls=CustomJSONEncoder, indent=4)
|
||||
|
||||
@transaction.atomic
|
||||
def shred_data(self):
|
||||
for o in self.event.orders.all():
|
||||
o.phone = None
|
||||
d = o.meta_info_data
|
||||
if d:
|
||||
if 'contact_form_data' in d and 'phone' in d['contact_form_data']:
|
||||
del d['contact_form_data']['phone']
|
||||
o.meta_info = json.dumps(d)
|
||||
o.save(update_fields=['meta_info', 'phone'])
|
||||
|
||||
for le in self.event.logentry_set.filter(action_type="pretix.event.order.phone.changed"):
|
||||
shred_log_fields(le, banlist=['old_phone', 'new_phone'])
|
||||
|
||||
|
||||
class EmailAddressShredder(BaseDataShredder):
|
||||
verbose_name = _('E-mails')
|
||||
identifier = 'order_emails'
|
||||
@@ -398,10 +372,9 @@ class PaymentInfoShredder(BaseDataShredder):
|
||||
|
||||
|
||||
@receiver(register_data_shredders, dispatch_uid="shredders_builtin")
|
||||
def register_core_shredders(sender, **kwargs):
|
||||
def register_payment_provider(sender, **kwargs):
|
||||
return [
|
||||
EmailAddressShredder,
|
||||
PhoneNumberShredder,
|
||||
AttendeeInfoShredder,
|
||||
InvoiceAddressShredder,
|
||||
QuestionAnswerShredder,
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
from django import template
|
||||
from phonenumber_field.phonenumber import PhoneNumber
|
||||
from phonenumbers import NumberParseException
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.filter("phone_format")
|
||||
def phone_format(value: str):
|
||||
if not value:
|
||||
return ""
|
||||
|
||||
if isinstance(value, str):
|
||||
try:
|
||||
return PhoneNumber.from_string(value).as_international
|
||||
except NumberParseException:
|
||||
return value
|
||||
|
||||
if isinstance(value, PhoneNumber) and value.national_number:
|
||||
return value.as_international
|
||||
|
||||
return str(value)
|
||||
@@ -1,4 +1,3 @@
|
||||
import re
|
||||
import urllib.parse
|
||||
|
||||
import bleach
|
||||
@@ -72,10 +71,6 @@ EMAIL_RE = build_email_re(tlds=sorted(tld_set, key=len, reverse=True))
|
||||
|
||||
|
||||
def safelink_callback(attrs, new=False):
|
||||
"""
|
||||
Makes sure that all links to a different domain are passed through a redirection handler
|
||||
to ensure there's no passing of referers with secrets inside them.
|
||||
"""
|
||||
url = attrs.get((None, 'href'), '/')
|
||||
if not url_has_allowed_host_and_scheme(url, allowed_hosts=None) and not url.startswith('mailto:') and not url.startswith('tel:'):
|
||||
signer = signing.Signer(salt='safe-redirect')
|
||||
@@ -85,42 +80,7 @@ def safelink_callback(attrs, new=False):
|
||||
return attrs
|
||||
|
||||
|
||||
def truelink_callback(attrs, new=False):
|
||||
"""
|
||||
Tries to prevent "phishing" attacks in which a link looks like it points to a safe place but instead
|
||||
points somewhere else, e.g.
|
||||
|
||||
<a href="https://evilsite.com">https://google.com</a>
|
||||
|
||||
At the same time, custom texts are still allowed:
|
||||
|
||||
<a href="https://maps.google.com">Get to the event</a>
|
||||
|
||||
Suffixes are also allowed:
|
||||
|
||||
<a href="https://maps.google.com/location/foo">https://maps.google.com</a>
|
||||
"""
|
||||
text = re.sub('[^a-zA-Z0-9.-/_]', '', attrs.get('_text')) # clean up link text
|
||||
if URL_RE.match(text):
|
||||
# link text looks like a url
|
||||
if text.startswith('//'):
|
||||
text = 'https:' + text
|
||||
elif not text.startswith('http'):
|
||||
text = 'https://' + text
|
||||
|
||||
text_url = urllib.parse.urlparse(text)
|
||||
href_url = urllib.parse.urlparse(attrs[None, 'href'])
|
||||
if text_url.netloc != href_url.netloc or not href_url.path.startswith(href_url.path):
|
||||
# link text contains an URL that has a different base than the actual URL
|
||||
attrs['_text'] = attrs[None, 'href']
|
||||
return attrs
|
||||
|
||||
|
||||
def abslink_callback(attrs, new=False):
|
||||
"""
|
||||
Makes sure that all links will be absolute links and will be opened in a new page with no
|
||||
window.opener attribute.
|
||||
"""
|
||||
url = attrs.get((None, 'href'), '/')
|
||||
if not url.startswith('mailto:') and not url.startswith('tel:'):
|
||||
attrs[None, 'href'] = urllib.parse.urljoin(settings.SITE_URL, url)
|
||||
@@ -133,7 +93,6 @@ def markdown_compile_email(source):
|
||||
linker = bleach.Linker(
|
||||
url_re=URL_RE,
|
||||
email_re=EMAIL_RE,
|
||||
callbacks=DEFAULT_CALLBACKS + [truelink_callback, abslink_callback],
|
||||
parse_email=True
|
||||
)
|
||||
return linker.linkify(bleach.clean(
|
||||
@@ -186,7 +145,7 @@ def rich_text(text: str, **kwargs):
|
||||
linker = bleach.Linker(
|
||||
url_re=URL_RE,
|
||||
email_re=EMAIL_RE,
|
||||
callbacks=DEFAULT_CALLBACKS + ([truelink_callback, safelink_callback] if kwargs.get('safelinks', True) else [truelink_callback, abslink_callback]),
|
||||
callbacks=DEFAULT_CALLBACKS + ([safelink_callback] if kwargs.get('safelinks', True) else [abslink_callback]),
|
||||
parse_email=True
|
||||
)
|
||||
body_md = linker.linkify(markdown_compile(text))
|
||||
@@ -202,7 +161,7 @@ def rich_text_snippet(text: str, **kwargs):
|
||||
linker = bleach.Linker(
|
||||
url_re=URL_RE,
|
||||
email_re=EMAIL_RE,
|
||||
callbacks=DEFAULT_CALLBACKS + ([truelink_callback, safelink_callback] if kwargs.get('safelinks', True) else [truelink_callback, abslink_callback]),
|
||||
callbacks=DEFAULT_CALLBACKS + ([safelink_callback] if kwargs.get('safelinks', True) else [abslink_callback]),
|
||||
parse_email=True
|
||||
)
|
||||
body_md = linker.linkify(markdown_compile(text, snippet=True))
|
||||
|
||||
@@ -13,11 +13,7 @@ class DownloadView(TemplateView):
|
||||
@cached_property
|
||||
def object(self) -> CachedFile:
|
||||
try:
|
||||
o = get_object_or_404(CachedFile, id=self.kwargs['id'], web_download=True)
|
||||
if o.session_key:
|
||||
if o.session_key != self.request.session.session_key:
|
||||
raise Http404()
|
||||
return o
|
||||
return get_object_or_404(CachedFile, id=self.kwargs['id'])
|
||||
except ValueError: # Invalid URLs
|
||||
raise Http404()
|
||||
|
||||
|
||||
@@ -203,7 +203,6 @@ class CachedFileField(ExtFileField):
|
||||
cf = CachedFile.objects.create(
|
||||
expires=now() + datetime.timedelta(days=1),
|
||||
date=now(),
|
||||
web_download=True,
|
||||
filename=data.name,
|
||||
type=data.content_type,
|
||||
)
|
||||
@@ -219,7 +218,6 @@ class CachedFileField(ExtFileField):
|
||||
if isinstance(data, File):
|
||||
cf = CachedFile.objects.create(
|
||||
expires=now() + datetime.timedelta(days=1),
|
||||
web_download=True,
|
||||
date=now(),
|
||||
filename=data.name,
|
||||
type=data.content_type,
|
||||
|
||||
@@ -19,7 +19,9 @@ 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
|
||||
@@ -35,6 +37,7 @@ from pretix.helpers.countries import CachedCountries
|
||||
from pretix.multidomain.models import KnownDomain
|
||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
||||
from pretix.plugins.banktransfer.payment import BankTransfer
|
||||
from pretix.presale.style import get_fonts
|
||||
|
||||
|
||||
class EventWizardFoundationForm(forms.Form):
|
||||
@@ -456,7 +459,6 @@ class EventSettingsForm(SettingsForm):
|
||||
'presale_start_show_date',
|
||||
'locales',
|
||||
'locale',
|
||||
'region',
|
||||
'show_quota_left',
|
||||
'waiting_list_enabled',
|
||||
'waiting_list_hours',
|
||||
@@ -480,9 +482,6 @@ class EventSettingsForm(SettingsForm):
|
||||
'attendee_addresses_asked',
|
||||
'attendee_addresses_required',
|
||||
'attendee_data_explanation_text',
|
||||
'order_phone_asked',
|
||||
'order_phone_required',
|
||||
'checkout_phone_helptext',
|
||||
'banner_text',
|
||||
'banner_text_bottom',
|
||||
'order_email_asked_twice',
|
||||
@@ -500,31 +499,6 @@ class EventSettingsForm(SettingsForm):
|
||||
data = super().clean()
|
||||
settings_dict = self.event.settings.freeze()
|
||||
settings_dict.update(data)
|
||||
|
||||
# set all dependants of virtual_keys and
|
||||
# delete all virtual_fields to prevent them from being saved
|
||||
for virtual_key in self.virtual_keys:
|
||||
if virtual_key not in data:
|
||||
continue
|
||||
base_key = virtual_key.rsplit('_', 2)[0]
|
||||
asked_key = base_key + '_asked'
|
||||
required_key = base_key + '_required'
|
||||
|
||||
if data[virtual_key] == 'optional':
|
||||
data[asked_key] = True
|
||||
data[required_key] = False
|
||||
elif data[virtual_key] == 'required':
|
||||
data[asked_key] = True
|
||||
data[required_key] = True
|
||||
# Explicitly check for 'do_not_ask'.
|
||||
# Do not overwrite as default-behaviour when no value for virtual field is transmitted!
|
||||
elif data[virtual_key] == 'do_not_ask':
|
||||
data[asked_key] = False
|
||||
data[required_key] = False
|
||||
|
||||
# hierarkey.forms cannot handle non-existent keys in cleaned_data => do not delete, but set to None
|
||||
data[virtual_key] = None
|
||||
|
||||
validate_event_settings(self.event, data)
|
||||
return data
|
||||
|
||||
@@ -548,39 +522,9 @@ class EventSettingsForm(SettingsForm):
|
||||
if not self.event.has_subevents:
|
||||
del self.fields['frontpage_subevent_ordering']
|
||||
del self.fields['event_list_type']
|
||||
|
||||
# create "virtual" fields for better UX when editing <name>_asked and <name>_required fields
|
||||
self.virtual_keys = []
|
||||
for asked_key in [key for key in self.fields.keys() if key.endswith('_asked')]:
|
||||
required_key = asked_key.rsplit('_', 1)[0] + '_required'
|
||||
virtual_key = asked_key + '_required'
|
||||
if required_key not in self.fields or virtual_key in self.fields:
|
||||
# either no matching required key or
|
||||
# there already is a field with virtual_key defined manually, so do not overwrite
|
||||
continue
|
||||
|
||||
asked_field = self.fields[asked_key]
|
||||
|
||||
self.fields[virtual_key] = forms.ChoiceField(
|
||||
label=asked_field.label,
|
||||
help_text=asked_field.help_text,
|
||||
required=True,
|
||||
widget=forms.RadioSelect,
|
||||
choices=[
|
||||
# default key needs a value other than '' because with '' it would also overwrite even if combi-field is not transmitted
|
||||
('do_not_ask', _('Do not ask')),
|
||||
('optional', _('Ask, but do not require input')),
|
||||
('required', _('Ask and require input'))
|
||||
]
|
||||
)
|
||||
self.virtual_keys.append(virtual_key)
|
||||
|
||||
if self.initial[required_key]:
|
||||
self.initial[virtual_key] = 'required'
|
||||
elif self.initial[asked_key]:
|
||||
self.initial[virtual_key] = 'optional'
|
||||
else:
|
||||
self.initial[virtual_key] = 'do_not_ask'
|
||||
self.fields['primary_font'].choices += [
|
||||
(a, {"title": a, "data": v}) for a, v in get_fonts().items()
|
||||
]
|
||||
|
||||
|
||||
class CancelSettingsForm(SettingsForm):
|
||||
|
||||
@@ -150,8 +150,8 @@ class OrderFilterForm(FilterForm):
|
||||
(Order.STATUS_PENDING + Order.STATUS_PAID, _('Pending or paid')),
|
||||
)),
|
||||
(_('Cancellations'), (
|
||||
(Order.STATUS_CANCELED, _('Canceled (fully)')),
|
||||
('cp', _('Canceled (fully or with paid fee)')),
|
||||
(Order.STATUS_CANCELED, _('Canceled')),
|
||||
('cp', _('Canceled (or with paid fee)')),
|
||||
('rc', _('Cancellation requested')),
|
||||
)),
|
||||
(_('Payment process'), (
|
||||
@@ -159,8 +159,7 @@ class OrderFilterForm(FilterForm):
|
||||
(Order.STATUS_PENDING + Order.STATUS_EXPIRED, _('Pending or expired')),
|
||||
('o', _('Pending (overdue)')),
|
||||
('overpaid', _('Overpaid')),
|
||||
('partially_paid', _('Partially paid')),
|
||||
('underpaid', _('Underpaid (but confirmed)')),
|
||||
('underpaid', _('Underpaid')),
|
||||
('pendingpaid', _('Pending (but fully paid)')),
|
||||
)),
|
||||
(_('Approval process'), (
|
||||
@@ -246,14 +245,6 @@ class OrderFilterForm(FilterForm):
|
||||
Q(status__in=(Order.STATUS_EXPIRED, Order.STATUS_PENDING)) & Q(pending_sum_t__lte=0)
|
||||
& Q(require_approval=False)
|
||||
)
|
||||
elif s == 'partially_paid':
|
||||
qs = Order.annotate_overpayments(qs, refunds=False, results=False, sums=True)
|
||||
qs = qs.filter(
|
||||
computed_payment_refund_sum__lt=F('total'),
|
||||
computed_payment_refund_sum__gt=Decimal('0.00')
|
||||
).exclude(
|
||||
status=Order.STATUS_CANCELED
|
||||
)
|
||||
elif s == 'underpaid':
|
||||
qs = Order.annotate_overpayments(qs, refunds=False, results=False, sums=True)
|
||||
qs = qs.filter(
|
||||
|
||||
@@ -10,15 +10,11 @@ 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(list(self.fields.items()) + [
|
||||
self.fields = OrderedDict([
|
||||
('footer_text', I18nFormField(
|
||||
widget=I18nTextInput,
|
||||
required=False,
|
||||
|
||||
@@ -16,7 +16,6 @@ from i18nfield.strings import LazyI18nString
|
||||
|
||||
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,
|
||||
)
|
||||
@@ -461,15 +460,7 @@ class OrderContactForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = Order
|
||||
fields = ['email', 'email_known_to_work', 'phone']
|
||||
widgets = {
|
||||
'phone': WrappedPhoneNumberPrefixWidget()
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if not self.instance.event.settings.order_phone_asked and not self.instance.phone:
|
||||
del self.fields['phone']
|
||||
fields = ['email', 'email_known_to_work']
|
||||
|
||||
|
||||
class OrderLocaleForm(forms.ModelForm):
|
||||
|
||||
@@ -223,7 +223,6 @@ class OrganizerSettingsForm(SettingsForm):
|
||||
'giftcard_length',
|
||||
'giftcard_expiry_years',
|
||||
'locales',
|
||||
'region',
|
||||
'event_team_provisioning',
|
||||
'primary_color',
|
||||
'theme_color_success',
|
||||
|
||||
@@ -292,8 +292,6 @@ def pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs):
|
||||
'pretix.event.order.denied': _('The order has been denied.'),
|
||||
'pretix.event.order.contact.changed': _('The email address has been changed from "{old_email}" '
|
||||
'to "{new_email}".'),
|
||||
'pretix.event.order.phone.changed': _('The phone number has been changed from "{old_phone}" '
|
||||
'to "{new_phone}".'),
|
||||
'pretix.event.order.locale.changed': _('The order locale has been changed.'),
|
||||
'pretix.event.order.invoice.generated': _('The invoice has been generated.'),
|
||||
'pretix.event.order.invoice.regenerated': _('The invoice has been regenerated.'),
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<script type="text/javascript" src="{% url 'javascript-catalog' lang=request.LANGUAGE_CODE %}"
|
||||
defer></script>
|
||||
{% else %}
|
||||
<script src="{% statici18n request.LANGUAGE_CODE %}" async></script>
|
||||
<script src="{% statici18n LANGUAGE_CODE %}" async></script>
|
||||
{% endif %}
|
||||
{% compress js %}
|
||||
<script type="text/javascript" src="{% static "jquery/js/jquery-2.1.1.min.js" %}"></script>
|
||||
|
||||
@@ -83,61 +83,22 @@
|
||||
{% bootstrap_field sform.locales layout="control" %}
|
||||
{% bootstrap_field sform.locale layout="control" %}
|
||||
{% bootstrap_field sform.timezone layout="control" %}
|
||||
{% bootstrap_field sform.region layout="control" %}
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>{% trans "Customer and attendee data" %}</legend>
|
||||
<h4>{% trans "Customer data (once per order)" %}</h4>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-3">
|
||||
{% trans "E-mail" %}
|
||||
</label>
|
||||
<div class="col-md-9">
|
||||
<div class="checkbox">
|
||||
<label><input type="checkbox" checked="checked" disabled="disabled"> {% trans "Ask and require input" %}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% bootstrap_field sform.order_email_asked_twice layout="control" %}
|
||||
{% bootstrap_field sform.order_phone_asked_required layout="control" %}
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-3">
|
||||
{% trans "Name and address" %}
|
||||
</label>
|
||||
<div class="col-md-9 static-form-row">
|
||||
<p>
|
||||
<a href="{% url "control:event.settings.invoice" event=request.event.slug organizer=request.organizer.slug %}#tab-0-1-open" target="_blank">
|
||||
{% trans "See invoice settings" %}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4>{% trans "Attendee data (once per admission ticket)" %}</h4>
|
||||
|
||||
{% bootstrap_field sform.attendee_names_asked_required layout="control" %}
|
||||
{% bootstrap_field sform.attendee_emails_asked_required layout="control" %}
|
||||
{% bootstrap_field sform.attendee_company_asked_required layout="control" %}
|
||||
{% bootstrap_field sform.attendee_addresses_asked_required layout="control" %}
|
||||
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-3">
|
||||
{% trans "Custom fields" %}
|
||||
</label>
|
||||
<div class="col-md-9 static-form-row">
|
||||
<p>
|
||||
<a href="{% url "control:event.items.questions" event=request.event.slug organizer=request.organizer.slug %}" target="_blank">
|
||||
{% trans "Manage questions" %}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% bootstrap_field sform.attendee_data_explanation_text layout="control" %}
|
||||
|
||||
<h4>{% trans "Other settings" %}</h4>
|
||||
<legend>{% trans "Attendee data" %}</legend>
|
||||
{% bootstrap_field sform.attendee_names_asked layout="control" %}
|
||||
{% bootstrap_field sform.attendee_names_required layout="control" %}
|
||||
{% bootstrap_field sform.name_scheme layout="control" %}
|
||||
{% bootstrap_field sform.name_scheme_titles layout="control" %}
|
||||
{% bootstrap_field sform.order_email_asked_twice layout="control" %}
|
||||
{% bootstrap_field sform.attendee_emails_asked layout="control" %}
|
||||
{% bootstrap_field sform.attendee_emails_required layout="control" %}
|
||||
{% bootstrap_field sform.attendee_company_asked layout="control" %}
|
||||
{% bootstrap_field sform.attendee_company_required layout="control" %}
|
||||
{% bootstrap_field sform.attendee_addresses_asked layout="control" %}
|
||||
{% bootstrap_field sform.attendee_addresses_required layout="control" %}
|
||||
{% bootstrap_field sform.checkout_show_copy_answers_button layout="control" %}
|
||||
{% bootstrap_field sform.attendee_data_explanation_text layout="control" %}
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>{% trans "Texts" %}</legend>
|
||||
@@ -216,7 +177,6 @@
|
||||
</div>
|
||||
|
||||
{% bootstrap_field sform.checkout_email_helptext layout="control" %}
|
||||
{% bootstrap_field sform.checkout_phone_helptext layout="control" %}
|
||||
{% bootstrap_field sform.banner_text layout="control" %}
|
||||
{% bootstrap_field sform.banner_text_bottom layout="control" %}
|
||||
</fieldset>
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
{% load rich_text %}
|
||||
{% load safelink %}
|
||||
{% load eventsignal %}
|
||||
{% load phone_format %}
|
||||
{% block title %}
|
||||
{% blocktrans trimmed with code=order.code %}
|
||||
Order details: {{ code }}
|
||||
@@ -202,15 +201,6 @@
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</dd>
|
||||
{% if order.phone or request.event.settings.order_phone_asked %}
|
||||
<dt>{% trans "Phone number" %}</dt>
|
||||
<dd>
|
||||
{{ order.phone|default_if_none:""|phone_format }}
|
||||
<a href="{% url "control:event.order.contact" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}" class="btn btn-default btn-xs">
|
||||
<span class="fa fa-edit"></span>
|
||||
</a>
|
||||
</dd>
|
||||
{% endif %}
|
||||
{% if invoices %}
|
||||
<dt>{% trans "Invoices" %}</dt>
|
||||
<dd>
|
||||
@@ -570,26 +560,6 @@
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
{% if order.status != "c" and order.total != payment_refund_sum %}
|
||||
<div class="row-fluid product-row cart-row">
|
||||
<div class="col-md-4 col-xs-6">
|
||||
{% trans "Successful payments" %}
|
||||
</div>
|
||||
<div class="col-md-3 col-xs-6 col-md-offset-5 price">
|
||||
{{ payment_refund_sum|money:event.currency }}
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="row-fluid product-row total text-danger">
|
||||
<div class="col-md-4 col-xs-6">
|
||||
<strong>{% trans "Pending total" %}</strong>
|
||||
</div>
|
||||
<div class="col-md-3 col-xs-6 col-md-offset-5 price">
|
||||
<strong>{{ order.pending_sum|money:event.currency }}</strong>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% eventsignal event "pretix.control.signals.order_info" order=order request=request %}
|
||||
|
||||
@@ -113,7 +113,7 @@
|
||||
<a href="?{% url_replace request 'ordering' '-datetime' %}"><i class="fa fa-caret-down"></i></a>
|
||||
<a href="?{% url_replace request 'ordering' 'datetime' %}"><i class="fa fa-caret-up"></i></a>
|
||||
</th>
|
||||
<th class="text-right flip">{% trans "Order paid / total" %}
|
||||
<th class="text-right flip">{% trans "Order total" %}
|
||||
<a href="?{% url_replace request 'ordering' '-total' %}"><i class="fa fa-caret-down"></i></a>
|
||||
<a href="?{% url_replace request 'ordering' 'total' %}"><i class="fa fa-caret-up"></i></a></th>
|
||||
<th class="text-right flip">{% trans "Positions" %}</th>
|
||||
@@ -141,11 +141,7 @@
|
||||
<br>{{ o.invoice_address.name }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<span class="fa fa-{{ o.sales_channel_obj.icon }} text-muted"
|
||||
data-toggle="tooltip" title="{% trans o.sales_channel_obj.verbose_name %}"></span>
|
||||
{{ o.datetime|date:"SHORT_DATETIME_FORMAT" }}
|
||||
</td>
|
||||
<td>{{ o.datetime|date:"SHORT_DATETIME_FORMAT" }}</td>
|
||||
<td class="text-right flip">
|
||||
{% if o.has_cancellation_request %}
|
||||
<span class="label label-warning">{% trans "CANCELLATION REQUESTED" %}</span>
|
||||
@@ -162,13 +158,6 @@
|
||||
{% elif o.is_pending_with_full_payment %}
|
||||
<span class="label label-danger">{% trans "FULLY PAID" %}</span>
|
||||
{% endif %}
|
||||
{% if o.computed_payment_refund_sum == o.total or o.computed_payment_refund_sum == 0 %}
|
||||
<span class="text-muted">
|
||||
{% endif %}
|
||||
{{ o.computed_payment_refund_sum|money:request.event.currency }} /
|
||||
{% if o.computed_payment_refund_sum == o.total or o.computed_payment_refund_sum == 0 %}
|
||||
</span>
|
||||
{% endif %}
|
||||
{{ o.total|money:request.event.currency }}
|
||||
</td>
|
||||
<td class="text-right flip">{{ o.pcnt|default_if_none:"0" }}</td>
|
||||
|
||||
@@ -45,7 +45,6 @@
|
||||
<fieldset>
|
||||
<legend>{% trans "Localization" %}</legend>
|
||||
{% bootstrap_field sform.locales layout="control" %}
|
||||
{% bootstrap_field sform.region layout="control" %}
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>{% trans "Shop design" %}</legend>
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
<label class="col-md-3 control-label" for="id_url">{% trans "Voucher link" %}</label>
|
||||
<div class="col-md-9">
|
||||
<input type="text" name="url"
|
||||
value="{% abseventurl request.event "presale:event.redeem" %}?voucher={{ voucher.code }}{% if voucher.subevent_id %}&subevent={{ voucher.subevent_id }}{% endif %}"
|
||||
value="{% abseventurl request.event "presale:event.redeem" %}?voucher={{ voucher.code }}&subevent={{ voucher.subevent_id }}"
|
||||
class="form-control"
|
||||
id="id_url" readonly>
|
||||
</div>
|
||||
|
||||
@@ -74,15 +74,13 @@ def login(request):
|
||||
backend = [b for b in backends if b.visible][0]
|
||||
if request.user.is_authenticated:
|
||||
next_url = backend.get_next_url(request) or 'control:index'
|
||||
if next_url and url_has_allowed_host_and_scheme(next_url, allowed_hosts=None):
|
||||
return redirect(next_url)
|
||||
return redirect(reverse('control:index'))
|
||||
return redirect(next_url)
|
||||
if request.method == 'POST':
|
||||
form = LoginForm(backend=backend, data=request.POST, request=request)
|
||||
form = LoginForm(backend=backend, data=request.POST)
|
||||
if form.is_valid() and form.user_cache and form.user_cache.auth_backend == backend.identifier:
|
||||
return process_login(request, form.user_cache, form.cleaned_data.get('keep_logged_in', False))
|
||||
else:
|
||||
form = LoginForm(backend=backend, request=request)
|
||||
form = LoginForm(backend=backend)
|
||||
ctx['form'] = form
|
||||
ctx['can_register'] = settings.PRETIX_REGISTRATION
|
||||
ctx['can_reset'] = settings.PRETIX_PASSWORD_RESET
|
||||
|
||||
@@ -19,6 +19,7 @@ from django.http import (
|
||||
)
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.urls import reverse
|
||||
from django.utils import translation
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext, gettext_lazy as _
|
||||
@@ -54,7 +55,6 @@ from pretix.multidomain.urlreverse import get_event_domain
|
||||
from pretix.plugins.stripe.payment import StripeSettingsHolder
|
||||
from pretix.presale.style import regenerate_css
|
||||
|
||||
from ...base.i18n import language
|
||||
from ...base.models.items import ItemMetaProperty
|
||||
from ...base.settings import SETTINGS_AFFECTING_CSS, LazyI18nStringList
|
||||
from ..logdisplay import OVERVIEW_BANLIST
|
||||
@@ -594,7 +594,7 @@ class MailSettings(EventSettingsViewMixin, EventSettingsFormView):
|
||||
)
|
||||
|
||||
if request.POST.get('test', '0').strip() == '1':
|
||||
backend = self.request.event.get_mail_backend(force_custom=True, timeout=10)
|
||||
backend = self.request.event.get_mail_backend(force_custom=True)
|
||||
try:
|
||||
backend.test(self.request.event.settings.mail_from)
|
||||
except Exception as e:
|
||||
@@ -659,7 +659,7 @@ class MailSettingsPreview(EventPermissionRequiredMixin, View):
|
||||
if matched is not None:
|
||||
idx = matched.group('idx')
|
||||
if idx in self.supported_locale:
|
||||
with language(self.supported_locale[idx], self.request.event.settings.region):
|
||||
with translation.override(self.supported_locale[idx]):
|
||||
msgs[self.supported_locale[idx]] = markdown_compile_email(
|
||||
v.format_map(self.placeholders(preview_item))
|
||||
)
|
||||
|
||||
@@ -25,7 +25,7 @@ class GeoCodeView(LoginRequiredMixin, View):
|
||||
gs = GlobalSettingsObject()
|
||||
if gs.settings.opencagedata_apikey:
|
||||
res = self._use_opencage(q)
|
||||
elif gs.settings.mapquest_apikey:
|
||||
if gs.settings.mapquest_apikey:
|
||||
res = self._use_mapquest(q)
|
||||
else:
|
||||
return JsonResponse({
|
||||
|
||||
@@ -153,18 +153,17 @@ class OrderList(OrderSearchMixin, EventPermissionRequiredMixin, PaginationMixin,
|
||||
annotated = {
|
||||
o['pk']: o
|
||||
for o in
|
||||
Order.annotate_overpayments(Order.objects, sums=True).filter(
|
||||
Order.annotate_overpayments(Order.objects).filter(
|
||||
pk__in=[o.pk for o in ctx['orders']]
|
||||
).annotate(
|
||||
pcnt=Subquery(s, output_field=IntegerField()),
|
||||
has_cancellation_request=Exists(CancellationRequest.objects.filter(order=OuterRef('pk')))
|
||||
).values(
|
||||
'pk', 'pcnt', 'is_overpaid', 'is_underpaid', 'is_pending_with_full_payment', 'has_external_refund',
|
||||
'has_pending_refund', 'has_cancellation_request', 'computed_payment_refund_sum'
|
||||
'has_pending_refund', 'has_cancellation_request'
|
||||
)
|
||||
}
|
||||
|
||||
scs = get_all_sales_channels()
|
||||
for o in ctx['orders']:
|
||||
if o.pk not in annotated:
|
||||
continue
|
||||
@@ -175,8 +174,6 @@ class OrderList(OrderSearchMixin, EventPermissionRequiredMixin, PaginationMixin,
|
||||
o.has_external_refund = annotated.get(o.pk)['has_external_refund']
|
||||
o.has_pending_refund = annotated.get(o.pk)['has_pending_refund']
|
||||
o.has_cancellation_request = annotated.get(o.pk)['has_cancellation_request']
|
||||
o.computed_payment_refund_sum = annotated.get(o.pk)['computed_payment_refund_sum']
|
||||
o.sales_channel_obj = scs[o.sales_channel]
|
||||
|
||||
if ctx['page_obj'].paginator.count < 1000:
|
||||
# Performance safeguard: Only count positions if the data set is small
|
||||
@@ -265,8 +262,6 @@ class OrderDetail(OrderView):
|
||||
ctx['overpaid'] = self.order.pending_sum * -1
|
||||
ctx['sales_channel'] = get_all_sales_channels().get(self.order.sales_channel)
|
||||
ctx['download_buttons'] = self.download_buttons
|
||||
ctx['payment_refund_sum'] = self.order.payment_refund_sum
|
||||
ctx['pending_sum'] = self.order.pending_sum
|
||||
return ctx
|
||||
|
||||
@cached_property
|
||||
@@ -671,7 +666,7 @@ class OrderCancellationRequestDelete(OrderView):
|
||||
}, user=self.request.user)
|
||||
|
||||
messages.success(self.request, _('The request has been removed. If you want, you can now inform the user.'))
|
||||
with language(self.order.locale, self.request.event.settings.region):
|
||||
with language(self.order.locale):
|
||||
return redirect(reverse('control:event.order.sendmail', kwargs={
|
||||
'event': self.request.event.slug,
|
||||
'organizer': self.request.event.organizer.slug,
|
||||
@@ -942,7 +937,7 @@ class OrderRefundView(OrderView):
|
||||
if giftcard_value and self.order.email:
|
||||
messages.success(self.request, _('A new gift card was created. You can now send the user their '
|
||||
'gift card code.'))
|
||||
with language(self.order.locale, self.request.event.settings.region):
|
||||
with language(self.order.locale):
|
||||
return redirect(reverse('control:event.order.sendmail', kwargs={
|
||||
'event': self.request.event.slug,
|
||||
'organizer': self.request.event.organizer.slug,
|
||||
@@ -1668,7 +1663,6 @@ class OrderContactChange(OrderView):
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
old_email = self.order.email
|
||||
old_phone = self.order.phone
|
||||
changed = False
|
||||
if self.form.is_valid():
|
||||
new_email = self.form.cleaned_data['email']
|
||||
@@ -1683,18 +1677,6 @@ class OrderContactChange(OrderView):
|
||||
user=self.request.user,
|
||||
)
|
||||
|
||||
new_phone = self.form.cleaned_data.get('phone')
|
||||
if new_phone != old_phone:
|
||||
changed = True
|
||||
self.order.log_action(
|
||||
'pretix.event.order.phone.changed',
|
||||
data={
|
||||
'old_phone': old_phone,
|
||||
'new_phone': self.form.cleaned_data['phone'],
|
||||
},
|
||||
user=self.request.user,
|
||||
)
|
||||
|
||||
if self.form.cleaned_data['regenerate_secrets']:
|
||||
changed = True
|
||||
self.order.secret = generate_secret()
|
||||
@@ -1797,7 +1779,7 @@ class OrderSendMail(EventPermissionRequiredMixin, OrderViewMixin, FormView):
|
||||
code=self.kwargs['code'].upper()
|
||||
)
|
||||
self.preview_output = {}
|
||||
with language(order.locale, self.request.event.settings.region):
|
||||
with language(order.locale):
|
||||
email_context = get_email_context(event=order.event, order=order)
|
||||
email_template = LazyI18nString(form.cleaned_data['message'])
|
||||
email_subject = str(form.cleaned_data['subject']).format_map(TolerantDict(email_context))
|
||||
@@ -1860,7 +1842,7 @@ class OrderPositionSendMail(OrderSendMail):
|
||||
attendee_email__isnull=False
|
||||
)
|
||||
self.preview_output = {}
|
||||
with language(position.order.locale, self.request.event.settings.region):
|
||||
with language(position.order.locale):
|
||||
email_context = get_email_context(event=position.order.event, order=position.order, position=position)
|
||||
email_template = LazyI18nString(form.cleaned_data['message'])
|
||||
email_subject = str(form.cleaned_data['subject']).format_map(TolerantDict(email_context))
|
||||
@@ -2064,9 +2046,9 @@ class ExportDoView(EventPermissionRequiredMixin, ExportMixin, AsyncAction, View)
|
||||
messages.error(self.request, _('There was a problem processing your input. See below for error details.'))
|
||||
return self.get(request, *args, **kwargs)
|
||||
|
||||
cf = CachedFile(web_download=True, session_key=request.session.session_key)
|
||||
cf = CachedFile()
|
||||
cf.date = now()
|
||||
cf.expires = now() + timedelta(hours=24)
|
||||
cf.expires = now() + timedelta(days=3)
|
||||
cf.save()
|
||||
return self.do(self.request.event.id, str(cf.id), self.exporter.identifier, self.exporter.form.cleaned_data)
|
||||
|
||||
|
||||
@@ -1242,9 +1242,9 @@ class ExportDoView(OrganizerPermissionRequiredMixin, ExportMixin, AsyncAction, V
|
||||
messages.error(self.request, _('There was a problem processing your input. See below for error details.'))
|
||||
return self.get(request, *args, **kwargs)
|
||||
|
||||
cf = CachedFile(web_download=True, session_key=request.session.session_key)
|
||||
cf = CachedFile()
|
||||
cf.date = now()
|
||||
cf.expires = now() + timedelta(hours=24)
|
||||
cf.expires = now() + timedelta(days=3)
|
||||
cf.save()
|
||||
return self.do(
|
||||
organizer=self.request.organizer.id,
|
||||
|
||||
@@ -137,7 +137,7 @@ class BaseEditorView(EventPermissionRequiredMixin, TemplateView):
|
||||
buffer = BytesIO()
|
||||
p.write(buffer)
|
||||
buffer.seek(0)
|
||||
c = CachedFile(web_download=True)
|
||||
c = CachedFile()
|
||||
c.expires = now() + timedelta(days=7)
|
||||
c.date = now()
|
||||
c.filename = 'background_preview.pdf'
|
||||
@@ -162,7 +162,7 @@ class BaseEditorView(EventPermissionRequiredMixin, TemplateView):
|
||||
"status": "error",
|
||||
"error": error
|
||||
})
|
||||
c = CachedFile(web_download=True)
|
||||
c = CachedFile()
|
||||
c.expires = now() + timedelta(days=7)
|
||||
c.date = now()
|
||||
c.filename = 'background_preview.pdf'
|
||||
@@ -188,7 +188,7 @@ class BaseEditorView(EventPermissionRequiredMixin, TemplateView):
|
||||
pass
|
||||
|
||||
if "preview" in request.POST:
|
||||
with rolledback_transaction(), language(request.event.settings.locale, request.event.settings.region):
|
||||
with rolledback_transaction(), language(request.event.settings.locale):
|
||||
p = self._get_preview_position()
|
||||
fname, mimet, data = self.generate(
|
||||
p,
|
||||
|
||||
@@ -75,7 +75,7 @@ class ShredExportView(RecentAuthenticationRequiredMixin, EventPermissionRequired
|
||||
if constr:
|
||||
return self.error(ShredError(self.get_error_url()))
|
||||
|
||||
return self.do(self.request.event.id, request.POST.getlist("shredder"), self.request.session.session_key)
|
||||
return self.do(self.request.event.id, request.POST.getlist("shredder"))
|
||||
|
||||
|
||||
class ShredDoView(RecentAuthenticationRequiredMixin, EventPermissionRequiredMixin, ShredderMixin, AsyncAction, View):
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
from django.core.cache import cache
|
||||
from django.utils.translation import get_language
|
||||
from django_countries import Countries
|
||||
from django_countries.fields import CountryField
|
||||
|
||||
from pretix.base.i18n import get_language_without_region
|
||||
|
||||
|
||||
class CachedCountries(Countries):
|
||||
_cached_lists = {}
|
||||
@@ -15,7 +14,7 @@ class CachedCountries(Countries):
|
||||
django-countries performs a unicode-aware sorting based on pyuca which is incredibly
|
||||
slow.
|
||||
"""
|
||||
cache_key = "countries:all:{}".format(get_language_without_region())
|
||||
cache_key = "countries:all:{}".format(get_language())
|
||||
if self.cache_subkey:
|
||||
cache_key += ":" + self.cache_subkey
|
||||
if cache_key in self._cached_lists:
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
# Date according to https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
|
||||
SHORT_DATE_FORMAT = 'm/d/Y'
|
||||
SHORT_DATETIME_FORMAT = 'm/d/Y P'
|
||||
TIME_FORMAT = 'P'
|
||||
WEEK_FORMAT = '\\W W, o'
|
||||
WEEK_DAY_FORMAT = 'D, M jS'
|
||||
|
||||
DATE_INPUT_FORMATS = [
|
||||
'%m/%d/%Y',
|
||||
'%Y-%m-%d',
|
||||
'%m/%d/%y',
|
||||
]
|
||||
TIME_INPUT_FORMATS = [
|
||||
'%I:%M %p',
|
||||
'%H:%M:%S', # '14:30:59'
|
||||
'%H:%M:%S.%f', # '14:30:59.000200'
|
||||
'%H:%M', # '14:30'
|
||||
]
|
||||
@@ -126,7 +126,6 @@ def get_javascript_format_without_seconds(format_name):
|
||||
|
||||
def get_moment_locale(locale=None):
|
||||
cur_lang = locale or translation.get_language()
|
||||
cur_lang = cur_lang.lower()
|
||||
if cur_lang in moment_locales:
|
||||
return cur_lang
|
||||
if '-' in cur_lang or '_' in cur_lang:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-12-22 11:06+0000\n"
|
||||
"POT-Creation-Date: 2020-11-27 17:32+0000\n"
|
||||
"PO-Revision-Date: 2020-07-30 19:00+0000\n"
|
||||
"Last-Translator: Abdullah <abdullah.gumaijan@gmail.com>\n"
|
||||
"Language-Team: Arabic <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
@@ -302,15 +302,15 @@ msgstr "الكل"
|
||||
msgid "None"
|
||||
msgstr "لا شيء"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:709
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:706
|
||||
msgid "Use a different name internally"
|
||||
msgstr "استخدام اسم مختلف داخليا"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:766
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:763
|
||||
msgid "Click to close"
|
||||
msgstr "انقر لقريب"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:781
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:778
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,17 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-12-22 11:06+0000\n"
|
||||
"PO-Revision-Date: 2020-12-19 07:00+0000\n"
|
||||
"Last-Translator: albert <albert.serra.monner@gmail.com>\n"
|
||||
"Language-Team: Catalan <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
"js/ca/>\n"
|
||||
"POT-Creation-Date: 2020-11-27 17:32+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: Automatically generated\n"
|
||||
"Language-Team: none\n"
|
||||
"Language: ca\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 3.10.3\n"
|
||||
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
|
||||
@@ -27,7 +25,7 @@ msgstr ""
|
||||
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:76
|
||||
msgid "Comment:"
|
||||
msgstr "Comentari:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:15
|
||||
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:39
|
||||
@@ -106,7 +104,7 @@ msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:189
|
||||
msgid "We are processing your request …"
|
||||
msgstr "Estem processant la vostra sol·licitud …"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:197
|
||||
msgid ""
|
||||
@@ -232,7 +230,7 @@ msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:477
|
||||
msgid "Ticket design"
|
||||
msgstr "Disseny del tiquet"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:734
|
||||
msgid "Saving failed."
|
||||
@@ -281,15 +279,15 @@ msgstr ""
|
||||
msgid "None"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:709
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:706
|
||||
msgid "Use a different name internally"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:766
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:763
|
||||
msgid "Click to close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:781
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:778
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr ""
|
||||
|
||||
@@ -303,7 +301,7 @@ msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/question.js:82
|
||||
msgid "Count"
|
||||
msgstr "Quantitat"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/question.js:135
|
||||
msgid "Yes"
|
||||
@@ -321,11 +319,11 @@ msgstr[1] ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/cart.js:39
|
||||
msgid "The items in your cart are no longer reserved for you."
|
||||
msgstr "El contingut de la cistella ja no el teniu reservat."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/cart.js:41
|
||||
msgid "Cart expired"
|
||||
msgstr "Cistella expirada"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/cart.js:46
|
||||
msgid "The items in your cart are reserved for you for one minute."
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,17 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-12-22 11:06+0000\n"
|
||||
"PO-Revision-Date: 2020-12-14 10:00+0000\n"
|
||||
"Last-Translator: Ondřej Sokol <osokol@treesoft.cz>\n"
|
||||
"Language-Team: Czech <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
"cs/>\n"
|
||||
"POT-Creation-Date: 2020-11-27 17:32+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: Automatically generated\n"
|
||||
"Language-Team: none\n"
|
||||
"Language: cs\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
|
||||
"X-Generator: Weblate 3.10.3\n"
|
||||
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
|
||||
@@ -281,15 +279,15 @@ msgstr ""
|
||||
msgid "None"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:709
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:706
|
||||
msgid "Use a different name internally"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:766
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:763
|
||||
msgid "Click to close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:781
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:778
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr ""
|
||||
|
||||
@@ -299,19 +297,19 @@ msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/question.js:42
|
||||
msgid "Others"
|
||||
msgstr "Další"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/question.js:82
|
||||
msgid "Count"
|
||||
msgstr "Počet"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/question.js:135
|
||||
msgid "Yes"
|
||||
msgstr "Ano"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/question.js:136
|
||||
msgid "No"
|
||||
msgstr "Ne"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/subevent.js:111
|
||||
msgid "(one more date)"
|
||||
@@ -363,12 +361,12 @@ msgstr ""
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
||||
msgctxt "widget"
|
||||
msgid "Sold out"
|
||||
msgstr "Vyprodáno"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:18
|
||||
msgctxt "widget"
|
||||
msgid "Buy"
|
||||
msgstr "Koupit"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:19
|
||||
msgctxt "widget"
|
||||
@@ -457,62 +455,62 @@ msgstr ""
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:36
|
||||
msgctxt "widget"
|
||||
msgid "Resume checkout"
|
||||
msgstr "Obnovit checkout"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:37
|
||||
msgctxt "widget"
|
||||
msgid "Redeem a voucher"
|
||||
msgstr "Uplatnit poukázku"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:38
|
||||
msgctxt "widget"
|
||||
msgid "Redeem"
|
||||
msgstr "Uplatnit"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:39
|
||||
msgctxt "widget"
|
||||
msgid "Voucher code"
|
||||
msgstr "Kód poukázky"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:40
|
||||
msgctxt "widget"
|
||||
msgid "Close"
|
||||
msgstr "Zavřít"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:41
|
||||
msgctxt "widget"
|
||||
msgid "Continue"
|
||||
msgstr "Pokračovat"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:42
|
||||
msgctxt "widget"
|
||||
msgid "See variations"
|
||||
msgstr "Zobrazit možnosti"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:43
|
||||
msgctxt "widget"
|
||||
msgid "Choose a different event"
|
||||
msgstr "Vybrat jinou událost"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:44
|
||||
msgctxt "widget"
|
||||
msgid "Choose a different date"
|
||||
msgstr "Vybrat jiný datum"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:45
|
||||
msgctxt "widget"
|
||||
msgid "Back"
|
||||
msgstr "Zpět"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:46
|
||||
msgctxt "widget"
|
||||
msgid "Next month"
|
||||
msgstr "Následující měsíc"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:47
|
||||
msgctxt "widget"
|
||||
msgid "Previous month"
|
||||
msgstr "Předchozí měsíc"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:48
|
||||
msgctxt "widget"
|
||||
@@ -531,76 +529,76 @@ msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:52
|
||||
msgid "Mo"
|
||||
msgstr "Po"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:53
|
||||
msgid "Tu"
|
||||
msgstr "Út"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:54
|
||||
msgid "We"
|
||||
msgstr "St"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:55
|
||||
msgid "Th"
|
||||
msgstr "Čt"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:56
|
||||
msgid "Fr"
|
||||
msgstr "Pá"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:57
|
||||
msgid "Sa"
|
||||
msgstr "So"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:58
|
||||
msgid "Su"
|
||||
msgstr "Ne"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:61
|
||||
msgid "January"
|
||||
msgstr "Leden"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:62
|
||||
msgid "February"
|
||||
msgstr "Únor"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:63
|
||||
msgid "March"
|
||||
msgstr "Březen"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:64
|
||||
msgid "April"
|
||||
msgstr "Duben"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:65
|
||||
msgid "May"
|
||||
msgstr "Květen"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:66
|
||||
msgid "June"
|
||||
msgstr "červen"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:67
|
||||
msgid "July"
|
||||
msgstr "Červenec"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:68
|
||||
msgid "August"
|
||||
msgstr "Srpen"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:69
|
||||
msgid "September"
|
||||
msgstr "Září"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:70
|
||||
msgid "October"
|
||||
msgstr "Říjen"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:71
|
||||
msgid "November"
|
||||
msgstr "Listopad"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:72
|
||||
msgid "December"
|
||||
msgstr "Prosinec"
|
||||
msgstr ""
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-12-22 11:06+0000\n"
|
||||
"POT-Creation-Date: 2020-11-27 17:32+0000\n"
|
||||
"PO-Revision-Date: 2020-09-15 02:00+0000\n"
|
||||
"Last-Translator: Mie Frydensbjerg <mif@aarhus.dk>\n"
|
||||
"Language-Team: Danish <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
@@ -306,15 +306,15 @@ msgstr "Alle"
|
||||
msgid "None"
|
||||
msgstr "Ingen"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:709
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:706
|
||||
msgid "Use a different name internally"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:766
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:763
|
||||
msgid "Click to close"
|
||||
msgstr "Klik for at lukke"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:781
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:778
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Du har ændringer, der ikke er gemt!"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-12-22 11:06+0000\n"
|
||||
"POT-Creation-Date: 2020-11-27 17:32+0000\n"
|
||||
"PO-Revision-Date: 2020-08-25 02:00+0000\n"
|
||||
"Last-Translator: Dennis Lichtenthäler <lichtenthaeler@rami.io>\n"
|
||||
"Language-Team: German <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
@@ -301,15 +301,15 @@ msgstr "Alle"
|
||||
msgid "None"
|
||||
msgstr "Keine"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:709
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:706
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Intern einen anderen Namen verwenden"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:766
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:763
|
||||
msgid "Click to close"
|
||||
msgstr "Klicken zum Schließen"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:781
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:778
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Sie haben ungespeicherte Änderungen!"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-12-22 11:06+0000\n"
|
||||
"POT-Creation-Date: 2020-11-27 17:32+0000\n"
|
||||
"PO-Revision-Date: 2020-08-25 02:00+0000\n"
|
||||
"Last-Translator: Dennis Lichtenthäler <lichtenthaeler@rami.io>\n"
|
||||
"Language-Team: German (informal) <https://translate.pretix.eu/projects/"
|
||||
@@ -300,15 +300,15 @@ msgstr "Alle"
|
||||
msgid "None"
|
||||
msgstr "Keine"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:709
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:706
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Intern einen anderen Namen verwenden"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:766
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:763
|
||||
msgid "Click to close"
|
||||
msgstr "Klicken zum Schließen"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:781
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:778
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Du hast ungespeicherte Änderungen!"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-12-22 11:06+0000\n"
|
||||
"POT-Creation-Date: 2020-11-27 17:32+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -280,15 +280,15 @@ msgstr ""
|
||||
msgid "None"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:709
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:706
|
||||
msgid "Use a different name internally"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:766
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:763
|
||||
msgid "Click to close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:781
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:778
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-12-22 11:06+0000\n"
|
||||
"POT-Creation-Date: 2020-11-27 17:32+0000\n"
|
||||
"PO-Revision-Date: 2019-10-03 19:00+0000\n"
|
||||
"Last-Translator: Chris Spy <chrispiropoulou@hotmail.com>\n"
|
||||
"Language-Team: Greek <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
@@ -320,15 +320,15 @@ msgstr "Όλα"
|
||||
msgid "None"
|
||||
msgstr "Κανένας"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:709
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:706
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Χρησιμοποιήστε διαφορετικό όνομα εσωτερικά"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:766
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:763
|
||||
msgid "Click to close"
|
||||
msgstr "Κάντε κλικ για να κλείσετε"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:781
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:778
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-12-22 11:06+0000\n"
|
||||
"POT-Creation-Date: 2020-11-27 17:32+0000\n"
|
||||
"PO-Revision-Date: 2020-04-27 20:00+0000\n"
|
||||
"Last-Translator: Gonzalo Gabriel Perez <zalitoar@gmail.com>\n"
|
||||
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
@@ -315,15 +315,15 @@ msgstr "Todos"
|
||||
msgid "None"
|
||||
msgstr "Ninguno"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:709
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:706
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Usar un nombre diferente internamente"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:766
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:763
|
||||
msgid "Click to close"
|
||||
msgstr "Click para cerrar"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:781
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:778
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "¡Tienes cambios sin guardar!"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-12-22 11:06+0000\n"
|
||||
"POT-Creation-Date: 2020-11-27 17:32+0000\n"
|
||||
"PO-Revision-Date: 2020-10-17 20:00+0000\n"
|
||||
"Last-Translator: Jaakko Rinta-Filppula <jaakko@r-f.fi>\n"
|
||||
"Language-Team: Finnish <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
@@ -283,15 +283,15 @@ msgstr "Kaikki"
|
||||
msgid "None"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:709
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:706
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Käytä toista nimeä sisäisesti"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:766
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:763
|
||||
msgid "Click to close"
|
||||
msgstr "Sulje klikkaamalla"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:781
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:778
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Sinulla on tallentamattomia muutoksia!"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: French\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-12-22 11:06+0000\n"
|
||||
"POT-Creation-Date: 2020-11-27 17:32+0000\n"
|
||||
"PO-Revision-Date: 2020-09-15 17:00+0000\n"
|
||||
"Last-Translator: Martin Gross <martin@pc-coholic.de>\n"
|
||||
"Language-Team: French <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
@@ -313,15 +313,15 @@ msgstr "Tous"
|
||||
msgid "None"
|
||||
msgstr "Aucun"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:709
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:706
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Utiliser un nom différent en interne"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:766
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:763
|
||||
msgid "Click to close"
|
||||
msgstr "Cliquez pour fermer"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:781
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:778
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Vous avez des modifications non sauvegardées !"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-12-22 11:06+0000\n"
|
||||
"POT-Creation-Date: 2020-11-27 17:32+0000\n"
|
||||
"PO-Revision-Date: 2020-01-24 08:00+0000\n"
|
||||
"Last-Translator: Prokaj Miklós <mixolid0@gmail.com>\n"
|
||||
"Language-Team: Hungarian <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
@@ -308,15 +308,15 @@ msgstr "Összes"
|
||||
msgid "None"
|
||||
msgstr "Semmi"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:709
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:706
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Használj másik nevet"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:766
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:763
|
||||
msgid "Click to close"
|
||||
msgstr "Bezárásért kattints"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:781
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:778
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Mentetlen változtatások!"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-12-22 11:06+0000\n"
|
||||
"POT-Creation-Date: 2020-11-27 17:32+0000\n"
|
||||
"PO-Revision-Date: 2020-06-12 20:00+0000\n"
|
||||
"Last-Translator: Frank <webappconcept@gmail.com>\n"
|
||||
"Language-Team: Italian <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
@@ -307,15 +307,15 @@ msgstr "Tutto"
|
||||
msgid "None"
|
||||
msgstr "Nessuno"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:709
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:706
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Utilizza un nome diverso internamente"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:766
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:763
|
||||
msgid "Click to close"
|
||||
msgstr "Clicca per chiudere"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:781
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:778
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Hai cambiamenti non salvati!"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-12-22 11:06+0000\n"
|
||||
"POT-Creation-Date: 2020-11-27 17:32+0000\n"
|
||||
"PO-Revision-Date: 2019-11-13 06:00+0000\n"
|
||||
"Last-Translator: Zane Smite <z.smite@riga-jurmala.com>\n"
|
||||
"Language-Team: Latvian <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
@@ -316,15 +316,15 @@ msgstr "Visi"
|
||||
msgid "None"
|
||||
msgstr "Neviens"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:709
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:706
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Izmantojiet citu nosaukumu iekšēji"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:766
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:763
|
||||
msgid "Click to close"
|
||||
msgstr "Noklikšķiniet, lai aizvērtu"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:781
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:778
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user