Compare commits

..

6 Commits

Author SHA1 Message Date
Raphael Michel
bb30c23341 Bump to 2023.7.3 2023-09-12 12:03:04 +02:00
Raphael Michel
edac1cf55b Move new settings to _base_settings 2023-09-12 11:57:15 +02:00
Raphael Michel
18ce0b3446 Bump to 2023.7.2 2023-09-12 11:50:22 +02:00
Raphael Michel
8583bfb7d9 [SECURITY] Do not allow Pillow to parse EPS files 2023-09-12 11:50:20 +02:00
Raphael Michel
34d1d3fa6e Bump to 2023.7.1 2023-09-11 09:58:10 +02:00
Raphael Michel
ccdce2ccb8 Fix incorrect handling of boolean configuration flags 2023-09-11 09:57:37 +02:00
78 changed files with 761 additions and 58137 deletions

View File

@@ -35,7 +35,7 @@ jobs:
- uses: actions/checkout@v2
- uses: harmon758/postgresql-action@v1
with:
postgresql version: '15'
postgresql version: '11'
postgresql db: 'pretix'
postgresql user: 'postgres'
postgresql password: 'postgres'

View File

@@ -1,4 +1,4 @@
from pretix.settings import *
LOGGING['handlers']['mail_admins']['include_html'] = True
STORAGES["staticfiles"]["BACKEND"] = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'

View File

@@ -37,7 +37,7 @@ you to execute a piece of code with a different locale:
This is very useful e.g. when sending an email to a user that has a different language than the user performing the
action that causes the mail to be sent.
.. _translation features: https://docs.djangoproject.com/en/4.2/topics/i18n/translation/
.. _translation features: https://docs.djangoproject.com/en/1.9/topics/i18n/translation/
.. _GNU gettext: https://www.gnu.org/software/gettext/
.. _strings: https://django-i18nfield.readthedocs.io/en/latest/strings.html
.. _database fields: https://django-i18nfield.readthedocs.io/en/latest/quickstart.html

View File

@@ -15,33 +15,25 @@ and the admin panel is available at ``https://pretix.eu/control/event/bigorg/awe
If the organizer now configures a custom domain like ``tickets.bigorg.com``, his event will
from now on be available on ``https://tickets.bigorg.com/awesomecon/``. The former URL at
``pretix.eu`` will redirect there. It's also possible to do this for just an event, in which
case the event will be available on ``https://tickets.awesomecon.org/``.
However, the admin panel will still only be available on ``pretix.eu`` for convenience and security reasons.
``pretix.eu`` will redirect there. However, the admin panel will still only be available
on ``pretix.eu`` for convenience and security reasons.
URL routing
-----------
The hard part about implementing this URL routing in Django is that
``https://pretix.eu/bigorg/awesomecon/`` contains two parameters of nearly arbitrary content
and ``https://tickets.bigorg.com/awesomecon/`` contains only one and ``https://tickets.awesomecon.org/`` does not contain any.
The only robust way to do this is by having *separate* URL configuration for those three cases.
and ``https://tickets.bigorg.com/awesomecon/`` contains only one. The only robust way to do
this is by having *separate* URL configuration for those two cases. In pretix, we call the
former our ``maindomain`` config and the latter our ``subdomain`` config. For pretix's core
modules we do some magic to avoid duplicate configuration, but for a fairly simple plugin with
only a handful of routes, we recommend just configuring the two URL sets separately.
In pretix, we therefore do not have a global URL configuration, but three, living in the following modules:
- ``pretix.multidomain.maindomain_urlconf``
- ``pretix.multidomain.organizer_domain_urlconf``
- ``pretix.multidomain.event_domain_urlconf``
We provide some helper utilities to work with these to avoid duplicate configuration of the individual URLs.
The file ``urls.py`` inside your plugin package will be loaded and scanned for URL configuration
automatically and should be provided by any plugin that provides any view.
However, unlike plain Django, we look not only for a ``urlpatterns`` attribute on the module but support other
attributes like ``event_patterns`` and ``organizer_patterns`` as well.
For example, for a simple plugin that adds one URL to the backend and one event-level URL to the frontend, you can
create the following configuration in your ``urls.py``::
A very basic example that provides one view in the admin panel and one view in the frontend
could look like this::
from django.urls import re_path
@@ -60,7 +52,7 @@ create the following configuration in your ``urls.py``::
As you can see, the view in the frontend is not included in the standard Django ``urlpatterns``
setting but in a separate list with the name ``event_patterns``. This will automatically prepend
the appropriate parameters to the regex (e.g. the event or the event and the organizer, depending
on the called domain). For organizer-level views, ``organizer_patterns`` works the same way.
on the called domain).
If you only provide URLs in the admin area, you do not need to provide a ``event_patterns`` attribute.
@@ -79,16 +71,11 @@ is a python method that emulates a behavior similar to ``reverse``:
.. autofunction:: pretix.multidomain.urlreverse.eventreverse
If you need to communicate the URL externally, you can use a different method to ensure that it is always an absolute URL:
.. autofunction:: pretix.multidomain.urlreverse.build_absolute_uri
In addition, there is a template tag that works similar to ``url`` but takes an event or organizer object
as its first argument and can be used like this::
{% load eventurl %}
<a href="{% eventurl request.event "presale:event.checkout" step="payment" %}">Pay</a>
<a href="{% abseventurl request.event "presale:event.checkout" step="payment" %}">Pay</a>
Implementation details

View File

@@ -36,7 +36,7 @@ dependencies = [
"css-inline==0.8.*",
"defusedcsv>=1.1.0",
"dj-static",
"Django==4.2.*",
"Django==4.1.*",
"django-bootstrap3==23.1.*",
"django-compressor==4.3.*",
"django-countries==7.5.*",

View File

@@ -19,4 +19,4 @@
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
__version__ = "2023.8.0.dev0"
__version__ = "2023.7.3"

View File

@@ -196,14 +196,7 @@ STATICFILES_DIRS = [
STATICI18N_ROOT = os.path.join(BASE_DIR, "pretix/static")
STORAGES = {
"default": {
"BACKEND": "django.core.files.storage.FileSystemStorage",
},
"staticfiles": {
"BACKEND": "django.contrib.staticfiles.storage.ManifestStaticFilesStorage",
},
}
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
# if os.path.exists(os.path.join(DATA_DIR, 'static')):
# STATICFILES_DIRS.insert(0, os.path.join(DATA_DIR, 'static'))
@@ -259,3 +252,20 @@ PRETIX_PRIMARY_COLOR = '#8E44B3'
# stressful for some cache setups so it is enabled by default and currently can't be enabled through pretix.cfg
CACHE_LARGE_VALUES_ALLOWED = False
CACHE_LARGE_VALUES_ALIAS = 'default'
# Allowed file extensions for various places plus matching Pillow formats.
# Never allow EPS, it is full of dangerous bugs.
FILE_UPLOAD_EXTENSIONS_IMAGE = (".png", ".jpg", ".gif", ".jpeg")
PILLOW_FORMATS_IMAGE = ('PNG', 'GIF', 'JPEG')
FILE_UPLOAD_EXTENSIONS_FAVICON = (".ico", ".png", "jpg", ".gif", ".jpeg")
FILE_UPLOAD_EXTENSIONS_QUESTION_IMAGE = (".png", "jpg", ".gif", ".jpeg", ".bmp", ".tif", ".tiff", ".jfif")
PILLOW_FORMATS_QUESTIONS_IMAGE = ('PNG', 'GIF', 'JPEG', 'BMP', 'TIFF')
FILE_UPLOAD_EXTENSIONS_EMAIL_ATTACHMENT = (
".png", ".jpg", ".gif", ".jpeg", ".pdf", ".txt", ".docx", ".gif", ".svg",
".pptx", ".ppt", ".doc", ".xlsx", ".xls", ".jfif", ".heic", ".heif", ".pages",
".bmp", ".tif", ".tiff"
)
FILE_UPLOAD_EXTENSIONS_OTHER = FILE_UPLOAD_EXTENSIONS_EMAIL_ATTACHMENT

View File

@@ -27,7 +27,6 @@ from decimal import Decimal
import pycountry
from django.conf import settings
from django.core.files import File
from django.db import models
from django.db.models import F, Q
from django.utils.encoding import force_str
from django.utils.timezone import now
@@ -373,15 +372,11 @@ class PdfDataSerializer(serializers.Field):
self.context['vars_images'] = get_images(self.context['event'])
for k, f in self.context['vars'].items():
if 'evaluate_bulk' in f:
# Will be evaluated later by our list serializers
res[k] = (f['evaluate_bulk'], instance)
else:
try:
res[k] = f['evaluate'](instance, instance.order, ev)
except:
logger.exception('Evaluating PDF variable failed')
res[k] = '(error)'
try:
res[k] = f['evaluate'](instance, instance.order, ev)
except:
logger.exception('Evaluating PDF variable failed')
res[k] = '(error)'
if not hasattr(ev, '_cached_meta_data'):
ev._cached_meta_data = ev.meta_data
@@ -434,38 +429,6 @@ class PdfDataSerializer(serializers.Field):
return res
class OrderPositionListSerializer(serializers.ListSerializer):
def to_representation(self, data):
# We have a custom implementation of this method because PdfDataSerializer() might keep some elements unevaluated
# with a (callable, input) tuple. We'll loop over these entries and evaluate them bulk-wise to save on SQL queries.
if isinstance(self.parent, OrderSerializer) and isinstance(self.parent.parent, OrderListSerializer):
# Do not execute our custom code because it will be executed by OrderListSerializer later for the
# full result set.
return super().to_representation(data)
iterable = data.all() if isinstance(data, models.Manager) else data
data = []
evaluate_queue = defaultdict(list)
for item in iterable:
entry = self.child.to_representation(item)
if "pdf_data" in entry:
for k, v in entry["pdf_data"].items():
if isinstance(v, tuple) and callable(v[0]):
evaluate_queue[v[0]].append((v[1], entry, k))
data.append(entry)
for func, entries in evaluate_queue.items():
results = func([item for (item, entry, k) in entries])
for (item, entry, k), result in zip(entries, results):
entry["pdf_data"][k] = result
return data
class OrderPositionSerializer(I18nAwareModelSerializer):
checkins = CheckinSerializer(many=True, read_only=True)
answers = AnswerSerializer(many=True)
@@ -477,7 +440,6 @@ class OrderPositionSerializer(I18nAwareModelSerializer):
attendee_name = serializers.CharField(required=False)
class Meta:
list_serializer_class = OrderPositionListSerializer
model = OrderPosition
fields = ('id', 'order', 'positionid', 'item', 'variation', 'price', 'attendee_name', 'attendee_name_parts',
'company', 'street', 'zipcode', 'city', 'country', 'state', 'discount',
@@ -506,20 +468,6 @@ class OrderPositionSerializer(I18nAwareModelSerializer):
def validate(self, data):
raise TypeError("this serializer is readonly")
def to_representation(self, data):
if isinstance(self.parent, (OrderListSerializer, OrderPositionListSerializer)):
# Do not execute our custom code because it will be executed by OrderListSerializer later for the
# full result set.
return super().to_representation(data)
entry = super().to_representation(data)
if "pdf_data" in entry:
for k, v in entry["pdf_data"].items():
if isinstance(v, tuple) and callable(v[0]):
entry["pdf_data"][k] = v[0]([v[1]])[0]
return entry
class RequireAttentionField(serializers.Field):
def to_representation(self, instance: OrderPosition):
@@ -665,34 +613,6 @@ class OrderURLField(serializers.URLField):
})
class OrderListSerializer(serializers.ListSerializer):
def to_representation(self, data):
# We have a custom implementation of this method because PdfDataSerializer() might keep some elements
# unevaluated with a (callable, input) tuple. We'll loop over these entries and evaluate them bulk-wise to
# save on SQL queries.
iterable = data.all() if isinstance(data, models.Manager) else data
data = []
evaluate_queue = defaultdict(list)
for item in iterable:
entry = self.child.to_representation(item)
for p in entry.get("positions", []):
if "pdf_data" in p:
for k, v in p["pdf_data"].items():
if isinstance(v, tuple) and callable(v[0]):
evaluate_queue[v[0]].append((v[1], p, k))
data.append(entry)
for func, entries in evaluate_queue.items():
results = func([item for (item, entry, k) in entries])
for (item, entry, k), result in zip(entries, results):
entry["pdf_data"][k] = result
return data
class OrderSerializer(I18nAwareModelSerializer):
invoice_address = InvoiceAddressSerializer(allow_null=True)
positions = OrderPositionSerializer(many=True, read_only=True)
@@ -707,7 +627,6 @@ class OrderSerializer(I18nAwareModelSerializer):
class Meta:
model = Order
list_serializer_class = OrderListSerializer
fields = (
'code', 'status', 'testmode', 'secret', 'email', 'phone', 'locale', 'datetime', 'expires', 'payment_date',
'payment_provider', 'fees', 'total', 'comment', 'custom_followup_at', 'invoice_address', 'positions', 'downloads',

View File

@@ -166,6 +166,7 @@ class InitializeView(APIView):
device.software_brand = serializer.validated_data.get('software_brand')
device.software_version = serializer.validated_data.get('software_version')
device.info = serializer.validated_data.get('info')
print(serializer.validated_data, request.data)
device.rsa_pubkey = serializer.validated_data.get('rsa_pubkey')
device.api_token = generate_api_token()
device.save()

View File

@@ -26,6 +26,7 @@ from decimal import Decimal
from zoneinfo import ZoneInfo
import django_filters
from django.conf import settings
from django.db import transaction
from django.db.models import (
Exists, F, OuterRef, Prefetch, Q, Subquery, prefetch_related_objects,
@@ -1191,7 +1192,7 @@ class OrderPositionViewSet(viewsets.ModelViewSet):
ftype, ignored = mimetypes.guess_type(image_file.name)
extension = os.path.basename(image_file.name).split('.')[-1]
else:
img = Image.open(image_file)
img = Image.open(image_file, formats=settings.PILLOW_FORMATS_QUESTIONS_IMAGE)
ftype = Image.MIME[img.format]
extensions = {
'GIF': 'gif', 'TIFF': 'tif', 'BMP': 'bmp', 'JPEG': 'jpg', 'PNG': 'png'

View File

@@ -500,14 +500,14 @@ class PortraitImageField(SizeValidationMixin, ExtValidationMixin, forms.FileFiel
file = BytesIO(data['content'])
try:
image = Image.open(file)
image = Image.open(file, formats=settings.PILLOW_FORMATS_QUESTIONS_IMAGE)
# verify() must be called immediately after the constructor.
image.verify()
# We want to do more than just verify(), so we need to re-open the file
if hasattr(file, 'seek'):
file.seek(0)
image = Image.open(file)
image = Image.open(file, formats=settings.PILLOW_FORMATS_QUESTIONS_IMAGE)
# load() is a potential DoS vector (see Django bug #18520), so we verify the size first
if image.width > 10_000 or image.height > 10_000:
@@ -566,7 +566,7 @@ class PortraitImageField(SizeValidationMixin, ExtValidationMixin, forms.FileFiel
return f
def __init__(self, *args, **kwargs):
kwargs.setdefault('ext_whitelist', (".png", ".jpg", ".jpeg", ".jfif", ".tif", ".tiff", ".bmp"))
kwargs.setdefault('ext_whitelist', settings.FILE_UPLOAD_EXTENSIONS_QUESTION_IMAGE)
kwargs.setdefault('max_size', settings.FILE_UPLOAD_MAX_SIZE_IMAGE)
super().__init__(*args, **kwargs)
@@ -826,11 +826,7 @@ class BaseQuestionsForm(forms.Form):
help_text=help_text,
initial=initial.file if initial else None,
widget=UploadedFileWidget(position=pos, event=event, answer=initial),
ext_whitelist=(
".png", ".jpg", ".gif", ".jpeg", ".pdf", ".txt", ".docx", ".gif", ".svg",
".pptx", ".ppt", ".doc", ".xlsx", ".xls", ".jfif", ".heic", ".heif", ".pages",
".bmp", ".tif", ".tiff"
),
ext_whitelist=settings.FILE_UPLOAD_EXTENSIONS_OTHER,
max_size=settings.FILE_UPLOAD_MAX_SIZE_OTHER,
)
elif q.type == Question.TYPE_DATE:

View File

@@ -271,8 +271,6 @@ class SecurityMiddleware(MiddlewareMixin):
(url.url_name == "event.checkout" and url.kwargs['step'] == "payment")
):
h['script-src'].append('https://pay.google.com')
h['frame-src'].append('https://pay.google.com')
h['connect-src'].append('https://google.com/pay')
if settings.LOG_CSP:
h['report-uri'] = ["/csp_report/"]
if 'Content-Security-Policy' in resp:

View File

@@ -97,7 +97,7 @@ def _transactions_mark_order_dirty(order_id, using=None):
if getattr(dirty_transactions, 'order_ids', None) is None:
dirty_transactions.order_ids = set()
if _check_for_dirty_orders not in [func for (savepoint_id, func, *__) in conn.run_on_commit]:
if _check_for_dirty_orders not in [func for savepoint_id, func in conn.run_on_commit]:
transaction.on_commit(_check_for_dirty_orders, using)
dirty_transactions.order_ids.clear() # This is necessary to clean up after old threads with rollbacked transactions

View File

@@ -88,7 +88,9 @@ class LogEntry(models.Model):
class Meta:
ordering = ('-datetime', '-id')
indexes = [models.Index(fields=["datetime", "id"])]
index_together = [
['datetime', 'id']
]
def display(self):
from ..signals import logentry_display

View File

@@ -121,10 +121,7 @@ class ReusableMedium(LoggedModel):
class Meta:
unique_together = (("identifier", "type", "organizer"),)
indexes = [
models.Index(fields=("identifier", "type", "organizer")),
models.Index(fields=("updated", "id")),
]
index_together = (("identifier", "type", "organizer"), ("updated", "id"))
ordering = "identifier", "type", "organizer"

View File

@@ -270,9 +270,9 @@ class Order(LockModel, LoggedModel):
verbose_name = _("Order")
verbose_name_plural = _("Orders")
ordering = ("-datetime", "-pk")
indexes = [
models.Index(fields=["datetime", "id"]),
models.Index(fields=["last_modified", "id"]),
index_together = [
["datetime", "id"],
["last_modified", "id"],
]
def __str__(self):
@@ -1246,7 +1246,7 @@ class QuestionAnswer(models.Model):
@property
def is_image(self):
return any(self.file.name.lower().endswith(e) for e in ('.jpg', '.png', '.gif', '.tiff', '.bmp', '.jpeg'))
return any(self.file.name.lower().endswith(e) for e in settings.FILE_UPLOAD_EXTENSIONS_QUESTION_IMAGE)
@property
def file_name(self):
@@ -1676,7 +1676,7 @@ class OrderPayment(models.Model):
"""
Marks the order as failed and sets info to ``info``, but only if the order is in ``created`` or ``pending``
state. This is equivalent to setting ``state`` to ``OrderPayment.PAYMENT_STATE_FAILED`` and logging a failure,
but it adds strong database locking since we do not want to report a failure for an order that has just
but it adds strong database logging since we do not want to report a failure for an order that has just
been marked as paid.
:param send_mail: Whether an email should be sent to the user about this event (default: ``True``).
"""
@@ -2756,8 +2756,8 @@ class Transaction(models.Model):
class Meta:
ordering = 'datetime', 'pk'
indexes = [
models.Index(fields=['datetime', 'id'])
index_together = [
['datetime', 'id']
]
def save(self, *args, **kwargs):

View File

@@ -805,7 +805,7 @@ class QuestionColumn(ImportColumn):
return self.q.clean_answer(value)
def assign(self, value, order, position, invoice_address, **kwargs):
if value is not None:
if value:
if not hasattr(order, '_answers'):
order._answers = []
if isinstance(value, QuestionOption):

View File

@@ -108,10 +108,7 @@ DEFAULT_VARIABLES = OrderedDict((
("positionid", {
"label": _("Order position number"),
"editor_sample": "1",
"evaluate": lambda orderposition, order, event: str(orderposition.positionid),
# There is no performance gain in using evaluate_bulk here, but we want to make sure it is used somewhere
# in core to make sure we notice if the implementation of the API breaks.
"evaluate_bulk": lambda orderpositions: [str(p.positionid) for p in orderpositions],
"evaluate": lambda orderposition, order, event: str(orderposition.positionid)
}),
("order_positionid", {
"label": _("Order code and position number"),
@@ -524,7 +521,7 @@ def images_from_questions(sender, *args, **kwargs):
else:
a = op.answers.filter(question_id=question_id).first() or a
if not a or not a.file or not any(a.file.name.lower().endswith(e) for e in (".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tif", ".tiff")):
if not a or not a.file or not any(a.file.name.lower().endswith(e) for e in settings.FILE_UPLOAD_EXTENSIONS_QUESTION_IMAGE):
return None
else:
if etag:

View File

@@ -2476,11 +2476,6 @@ class OrderChangeManager:
split_order.status = Order.STATUS_PAID
else:
split_order.status = Order.STATUS_PENDING
if self.order.status == Order.STATUS_PAID:
split_order.set_expires(
now(),
list(set(p.subevent_id for p in split_positions))
)
split_order.save()
if offset_amount > Decimal('0.00'):

View File

@@ -2793,7 +2793,7 @@ Your {organizer} team""")) # noqa: W291
'form_class': ExtFileField,
'form_kwargs': dict(
label=_('Header image'),
ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"),
ext_whitelist=settings.FILE_UPLOAD_EXTENSIONS_IMAGE,
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
help_text=_('If you provide a logo image, we will by default not show your event name and date '
'in the page header. By default, we show your logo with a size of up to 1140x120 pixels. You '
@@ -2836,7 +2836,7 @@ Your {organizer} team""")) # noqa: W291
'form_class': ExtFileField,
'form_kwargs': dict(
label=_('Header image'),
ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"),
ext_whitelist=settings.FILE_UPLOAD_EXTENSIONS_IMAGE,
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
help_text=_('If you provide a logo image, we will by default not show your organization name '
'in the page header. By default, we show your logo with a size of up to 1140x120 pixels. You '
@@ -2876,7 +2876,7 @@ Your {organizer} team""")) # noqa: W291
'form_class': ExtFileField,
'form_kwargs': dict(
label=_('Social media image'),
ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"),
ext_whitelist=settings.FILE_UPLOAD_EXTENSIONS_IMAGE,
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
help_text=_('This picture will be used as a preview if you post links to your ticket shop on social media. '
'Facebook advises to use a picture size of 1200 x 630 pixels, however some platforms like '
@@ -2897,7 +2897,7 @@ Your {organizer} team""")) # noqa: W291
'form_class': ExtFileField,
'form_kwargs': dict(
label=_('Logo image'),
ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"),
ext_whitelist=settings.FILE_UPLOAD_EXTENSIONS_IMAGE,
required=False,
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
help_text=_('We will show your logo with a maximal height and width of 2.5 cm.')

View File

@@ -683,16 +683,12 @@ dictionaries as values that contain keys like in the following example::
"product": {
"label": _("Product name"),
"editor_sample": _("Sample product"),
"evaluate": lambda orderposition, order, event: str(orderposition.item),
"evaluate_bulk": lambda orderpositions: [str(op.item) for op in orderpositions],
"evaluate": lambda orderposition, order, event: str(orderposition.item)
}
}
The ``evaluate`` member will be called with the order position, order and event as arguments. The event might
also be a subevent, if applicable.
The ``evaluate_bulk`` member is optional but can significantly improve performance in some situations because you
can perform database fetches in bulk instead of single queries for every position.
"""

View File

@@ -127,7 +127,7 @@ class ClearableBasenameFileInput(forms.ClearableFileInput):
@property
def is_img(self):
return any(self.file.name.lower().endswith(e) for e in ('.jpg', '.jpeg', '.png', '.gif'))
return any(self.file.name.lower().endswith(e) for e in settings.FILE_UPLOAD_EXTENSIONS_IMAGE)
def __str__(self):
if hasattr(self.file, 'display_name'):

View File

@@ -1732,8 +1732,8 @@ class CheckinListAttendeeFilterForm(FilterForm):
'-timestamp': (OrderBy(F('last_entry'), nulls_last=True, descending=True), '-order__code'),
'item': ('item__name', 'variation__value', 'order__code'),
'-item': ('-item__name', '-variation__value', '-order__code'),
'seat': ('seat__sorting_rank', 'seat__seat_guid'),
'-seat': ('-seat__sorting_rank', '-seat__seat_guid'),
'seat': ('seat__sorting_rank', 'seat__guid'),
'-seat': ('-seat__sorting_rank', '-seat__guid'),
'date': ('subevent__date_from', 'subevent__id', 'order__code'),
'-date': ('-subevent__date_from', 'subevent__id', '-order__code'),
'name': {'_order': F('display_name').asc(nulls_first=True),
@@ -1940,7 +1940,7 @@ class VoucherFilterForm(FilterForm):
'item__category__position',
'item__category',
'item__position',
'variation__position',
'item__variation__position',
'quota__name',
),
'subevent': 'subevent__date_from',
@@ -1950,7 +1950,7 @@ class VoucherFilterForm(FilterForm):
'-item__category__position',
'-item__category',
'-item__position',
'-variation__position',
'-item__variation__position',
'-quota__name',
)
}

View File

@@ -420,7 +420,7 @@ class OrganizerSettingsForm(SettingsForm):
organizer_logo_image = ExtFileField(
label=_('Header image'),
ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"),
ext_whitelist=settings.FILE_UPLOAD_EXTENSIONS_IMAGE,
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
required=False,
help_text=_('If you provide a logo image, we will by default not show your organization name '
@@ -430,7 +430,7 @@ class OrganizerSettingsForm(SettingsForm):
)
favicon = ExtFileField(
label=_('Favicon'),
ext_whitelist=(".ico", ".png", ".jpg", ".gif", ".jpeg"),
ext_whitelist=settings.FILE_UPLOAD_EXTENSIONS_FAVICON,
required=False,
max_size=settings.FILE_UPLOAD_MAX_SIZE_FAVICON,
help_text=_('If you provide a favicon, we will show it instead of the default pretix icon. '

View File

@@ -341,7 +341,6 @@ def pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs):
'pretix.giftcards.acceptance.added': _('Gift card acceptance for another organizer has been added.'),
'pretix.giftcards.acceptance.removed': _('Gift card acceptance for another organizer has been removed.'),
'pretix.giftcards.acceptance.acceptor.invited': _('A new gift card acceptor has been invited.'),
'pretix.giftcards.acceptance.acceptor.removed': _('A gift card acceptor has been removed.'),
'pretix.giftcards.acceptance.issuer.removed': _('A gift card issuer has been removed or declined.'),
'pretix.giftcards.acceptance.issuer.accepted': _('A new gift card issuer has been accepted.'),
'pretix.webhook.created': _('The webhook has been created.'),

View File

@@ -1,27 +0,0 @@
{% load i18n %}
<ul class="dropdown-menu dropdown-menu-right">
<li>
<a href="{% url "control:event.qrcode" event=request.event.slug organizer=request.organizer.slug filetype="png" %}{% if url %}?url={{ url|urlencode }}{% endif %}"
target="_blank" download>
{% blocktrans with filetype="PNG" %}Download QR code as {{ filetype }} image{% endblocktrans %}
</a>
</li>
<li>
<a href="{% url "control:event.qrcode" event=request.event.slug organizer=request.organizer.slug filetype="svg" %}{% if url %}?url={{ url|urlencode }}{% endif %}"
target="_blank" download>
{% blocktrans with filetype="SVG" %}Download QR code as {{ filetype }} image{% endblocktrans %}
</a>
</li>
<li>
<a href="{% url "control:event.qrcode" event=request.event.slug organizer=request.organizer.slug filetype="jpeg" %}{% if url %}?url={{ url|urlencode }}{% endif %}"
target="_blank" download>
{% blocktrans with filetype="JPG" %}Download QR code as {{ filetype }} image{% endblocktrans %}
</a>
</li>
<li>
<a href="{% url "control:event.qrcode" event=request.event.slug organizer=request.organizer.slug filetype="gif" %}{% if url %}?url={{ url|urlencode }}{% endif %}"
target="_blank" download>
{% blocktrans with filetype="GIF" %}Download QR code as {{ filetype }} image{% endblocktrans %}
</a>
</li>
</ul>

View File

@@ -27,7 +27,28 @@
<button type="button" class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown" title="{% trans "Create QR code" %}" aria-haspopup="true" aria-expanded="false">
<i class="fa fa-qrcode" aria-hidden="true"></i>
</button>
{% include "pretixcontrol/event/fragment_qr_dropdown.html" with url=0 %}
<ul class="dropdown-menu dropdown-menu-right">
<li>
<a href="{% url "control:event.qrcode" event=request.event.slug organizer=request.organizer.slug filetype="png" %}" target="_blank" download>
{% blocktrans with filetype="PNG" %}Download QR code as {{ filetype }} image{% endblocktrans %}
</a>
</li>
<li>
<a href="{% url "control:event.qrcode" event=request.event.slug organizer=request.organizer.slug filetype="svg" %}" target="_blank" download>
{% blocktrans with filetype="SVG" %}Download QR code as {{ filetype }} image{% endblocktrans %}
</a>
</li>
<li>
<a href="{% url "control:event.qrcode" event=request.event.slug organizer=request.organizer.slug filetype="jpeg" %}" target="_blank" download>
{% blocktrans with filetype="JPG" %}Download QR code as {{ filetype }} image{% endblocktrans %}
</a>
</li>
<li>
<a href="{% url "control:event.qrcode" event=request.event.slug organizer=request.organizer.slug filetype="gif" %}" target="_blank" download>
{% blocktrans with filetype="GIF" %}Download QR code as {{ filetype }} image{% endblocktrans %}
</a>
</li>
</ul>
</div>
<div class="clearfix"></div>
</div>

View File

@@ -337,7 +337,7 @@
<div class="alert alert-info">
{% blocktrans trimmed %}
The waiting list currently is not compatible with some advanced features of pretix such as
hidden products, add-on products or product bundles.
add-on products or product bundles.
{% endblocktrans %}
</div>
<div class="alert alert-info">

View File

@@ -69,7 +69,7 @@
<td></td>
<td class="text-right flip">
<strong>
{{ sums.sum_count }}
{{ sums.count }}
</strong>
</td>
<td></td>

View File

@@ -295,11 +295,6 @@
{% bootstrap_field sform.invoice_regenerate_allowed layout="control" %}
</fieldset>
</div>
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}
</button>
</div>
</div>
<div class="col-xs-12 col-lg-2">
<div class="panel panel-default">
@@ -312,5 +307,10 @@
</div>
</div>
</div>
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}
</button>
</div>
</form>
{% endblock %}

View File

@@ -42,18 +42,10 @@
<div class="form-group">
<label class="col-md-3 control-label" for="id_url">{% trans "Voucher link" %}</label>
<div class="col-md-9">
<div class="input-group">
<input type="text" name="url"
value="{{ url }}"
class="form-control"
id="id_url" readonly>
<div class="input-group-btn">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" title="{% trans "Create QR code" %}" aria-haspopup="true" aria-expanded="false">
<i class="fa fa-qrcode" aria-hidden="true"></i>
</button>
{% include "pretixcontrol/event/fragment_qr_dropdown.html" with url=url %}
</div>
</div>
<input type="text" name="url"
value="{% abseventurl request.event "presale:event.redeem" %}?voucher={{ voucher.code|urlencode }}{% if voucher.subevent_id %}&subevent={{ voucher.subevent_id }}{% endif %}"
class="form-control"
id="id_url" readonly>
</div>
</div>
{% endif %}

View File

@@ -40,7 +40,7 @@ from collections import OrderedDict
from decimal import Decimal
from io import BytesIO
from itertools import groupby
from urllib.parse import urlparse, urlsplit
from urllib.parse import urlsplit
from zoneinfo import ZoneInfo
import bleach
@@ -50,7 +50,6 @@ from django.apps import apps
from django.conf import settings
from django.contrib import messages
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import PermissionDenied
from django.core.files import File
from django.db import transaction
from django.db.models import ProtectedError
@@ -62,7 +61,6 @@ from django.http import (
from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse
from django.utils.functional import cached_property
from django.utils.http import url_has_allowed_host_and_scheme
from django.utils.timezone import now
from django.utils.translation import gettext, gettext_lazy as _, gettext_noop
from django.views.generic import FormView, ListView
@@ -1532,12 +1530,6 @@ class EventQRCode(EventPermissionRequiredMixin, View):
def get(self, request, *args, filetype, **kwargs):
url = build_absolute_uri(request.event, 'presale:event.index')
if "url" in request.GET:
if url_has_allowed_host_and_scheme(request.GET["url"], allowed_hosts=[urlparse(url).netloc]):
url = request.GET["url"]
else:
raise PermissionDenied("Untrusted URL")
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_M,

View File

@@ -418,7 +418,7 @@ class OrderTransactions(OrderView):
'item', 'variation', 'subevent'
).order_by('datetime')
ctx['sums'] = self.order.transactions.aggregate(
sum_count=Sum('count'),
count=Sum('count'),
full_price=Sum(F('count') * F('price')),
full_tax_value=Sum(F('count') * F('tax_value')),
)

View File

@@ -546,7 +546,7 @@ def variations_select2(request, **kwargs):
F('item__category__position').asc(nulls_first=True),
'item__category_id',
'item__position',
'item__pk',
'item__pk'
'position',
'value'
).select_related('item')
@@ -718,7 +718,7 @@ def itemvarquota_select2(request, **kwargs):
itemqs = request.event.items.prefetch_related('variations').filter(
Q(name__icontains=i18ncomp(query)) | Q(internal_name__icontains=query)
)
quotaqs = request.event.quotas.filter(quotaf).select_related('subevent').order_by('-subevent__date_from', 'name')
quotaqs = request.event.quotas.filter(quotaf).select_related('subevent')
more = False
else:
if page == 1:
@@ -727,7 +727,7 @@ def itemvarquota_select2(request, **kwargs):
)
else:
itemqs = request.event.items.none()
quotaqs = request.event.quotas.filter(name__icontains=query).select_related('subevent').order_by('-subevent__date_from', 'name')
quotaqs = request.event.quotas.filter(name__icontains=query).select_related('subevent')
total = quotaqs.count()
pagesize = 20
offset = (page - 1) * pagesize

View File

@@ -34,7 +34,6 @@
# License for the specific language governing permissions and limitations under the License.
import io
from urllib.parse import urlencode
import bleach
from defusedcsv import csv
@@ -76,7 +75,6 @@ from pretix.control.views import PaginationMixin
from pretix.helpers.compat import CompatDeleteView
from pretix.helpers.format import format_map
from pretix.helpers.models import modelcopy
from pretix.multidomain.urlreverse import build_absolute_uri
class VoucherList(PaginationMixin, EventPermissionRequiredMixin, ListView):
@@ -317,13 +315,6 @@ class VoucherUpdate(EventPermissionRequiredMixin, UpdateView):
expires__gte=now()
).count()
ctx['redeemed_in_carts'] = redeemed_in_carts
url_params = {
'voucher': self.object.code
}
if self.object.subevent_id:
url_params['subevent'] = self.object.subevent_id
ctx['url'] = build_absolute_uri(self.request.event, "presale:event.redeem") + "?" + urlencode(url_params)
return ctx

View File

@@ -22,6 +22,7 @@
import logging
from io import BytesIO
from django.conf import settings
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
from PIL.Image import MAX_IMAGE_PIXELS, DecompressionBombError
@@ -51,7 +52,7 @@ def validate_uploaded_file_for_valid_image(f):
try:
try:
image = Image.open(file)
image = Image.open(file, formats=settings.PILLOW_FORMATS_QUESTIONS_IMAGE)
# verify() must be called immediately after the constructor.
image.verify()
except DecompressionBombError:

View File

@@ -21,6 +21,8 @@
#
from datetime import datetime
from PIL import Image
def monkeypatch_vobject_performance():
"""
@@ -52,5 +54,19 @@ def monkeypatch_vobject_performance():
icalendar.tzinfo_eq = new_tzinfo_eq
def monkeypatch_pillow_safer():
"""
Pillow supports many file formats, among them EPS. For EPS, Pillow loads GhostScript whenever GhostScript
is installed (cannot officially be disabled). However, GhostScript is known for regular security vulnerabilities.
We have no use of reading EPS files and usually prevent this by using `Image.open(…, formats=[…])` to disable EPS
support explicitly. However, we are worried about our dependencies like reportlab using `Image.open` without the
`formats=` parameter. Therefore, as a defense in depth approach, we monkeypatch EPS support away by modifying the
internal image format registry of Pillow.
"""
if "EPS" in Image.ID:
Image.ID.remove("EPS")
def monkeypatch_all_at_ready():
monkeypatch_vobject_performance()
monkeypatch_pillow_safer()

View File

@@ -20,8 +20,9 @@
# <https://www.gnu.org/licenses/>.
#
from arabic_reshaper import ArabicReshaper
from django.conf import settings
from django.utils.functional import SimpleLazyObject
from PIL.Image import Resampling
from PIL import Image
from reportlab.lib.utils import ImageReader
@@ -33,7 +34,7 @@ class ThumbnailingImageReader(ImageReader):
height = width * self._image.size[1] / self._image.size[0]
self._image.thumbnail(
size=(int(width * dpi / 72), int(height * dpi / 72)),
resample=Resampling.BICUBIC
resample=Image.Resampling.BICUBIC
)
self._data = None
return width, height
@@ -44,6 +45,9 @@ class ThumbnailingImageReader(ImageReader):
# (smaller) size of the modified image.
return None
def _read_image(self, fp):
return Image.open(fp, formats=settings.PILLOW_FORMATS_IMAGE)
reshaper = SimpleLazyObject(lambda: ArabicReshaper(configuration={
'delete_harakat': True,

View File

@@ -23,6 +23,7 @@ import hashlib
import math
from io import BytesIO
from django.conf import settings
from django.core.files.base import ContentFile
from django.core.files.storage import default_storage
from PIL import Image, ImageOps, ImageSequence
@@ -165,7 +166,7 @@ def resize_image(image, size):
def create_thumbnail(sourcename, size):
source = default_storage.open(sourcename)
image = Image.open(BytesIO(source.read()))
image = Image.open(BytesIO(source.read()), formats=settings.PILLOW_FORMATS_QUESTIONS_IMAGE)
try:
image.load()
except:

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: 1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-07-27 11:49+0000\n"
"PO-Revision-Date: 2023-07-28 09:11+0000\n"
"PO-Revision-Date: 2023-07-27 11:58+0000\n"
"Last-Translator: Raphael Michel <michel@rami.io>\n"
"Language-Team: German (informal) <https://translate.pretix.eu/projects/"
"pretix/pretix/de_Informal/>\n"
@@ -16172,7 +16172,7 @@ msgstr "Kontoeinstellungen"
#: pretix/presale/templates/pretixpresale/fragment_login_status.html:13
#: pretix/presale/templates/pretixpresale/fragment_login_status.html:14
msgid "Log out"
msgstr "Abmelden"
msgstr "Anmelden"
#: pretix/control/templates/pretixcontrol/base.html:245
msgid "Organizer account"

File diff suppressed because it is too large Load Diff

View File

@@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-07-27 11:49+0000\n"
"PO-Revision-Date: 2023-08-02 02:00+0000\n"
"Last-Translator: Patrizia Cotza <str.cotza@gmail.com>\n"
"PO-Revision-Date: 2023-07-13 07:22+0000\n"
"Last-Translator: Martin Gross <gross@rami.io>\n"
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/pretix/"
"es/>\n"
"Language: es\n"
@@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.18.2\n"
"X-Generator: Weblate 4.17\n"
#: pretix/_base_settings.py:78
msgid "English"
@@ -2435,8 +2435,6 @@ msgstr "Disponibilidad de cuotas"
msgid ""
"Download a spreadsheet of all quotas including their current availability."
msgstr ""
"Descargar un archivo Excel con todas las cuotas incluyendo su disponibilidad "
"actual."
#: pretix/base/exporters/orderlist.py:1082
#: pretix/control/templates/pretixcontrol/items/quotas.html:45
@@ -2501,7 +2499,6 @@ msgstr "Tarjeta de regalo"
#: pretix/base/exporters/orderlist.py:1132
msgid "Download a spreadsheet of all gift card transactions."
msgstr ""
"Descargar una hoja de cálculo con todas las transacciones de tarjeta regalo."
#: pretix/base/exporters/orderlist.py:1160
#: pretix/base/exporters/orderlist.py:1207
@@ -2574,8 +2571,6 @@ msgstr "Redenciones de tarjetas de regalo"
msgid ""
"Download a spreadsheet of all payments or refunds that involve gift cards."
msgstr ""
"Descargar una hoja de cálculo con todos los pagos y devoluciones que "
"contienen tarjetas regalo."
#: pretix/base/exporters/orderlist.py:1207
#: pretix/control/templates/pretixcontrol/giftcards/payment.html:16
@@ -2782,8 +2777,6 @@ msgid ""
"Due to technical reasons you cannot set inputs, that need to be masked (e.g. "
"passwords), to %(value)s."
msgstr ""
"Por razones técnicas, no puedes introducir datos que deben estar ocultos ("
"ej. contraseñas) en el campo %(value)s."
#: pretix/base/forms/auth.py:57 pretix/base/forms/auth.py:168
msgid "Keep me logged in"
@@ -2855,8 +2848,6 @@ msgid ""
"You uploaded an image in landscape orientation. Please upload an image in "
"portrait orientation."
msgstr ""
"Has cargado una imagen con formato horizontal. Por favor sube una imagen en "
"vertical."
#: pretix/base/forms/questions.py:471
msgid "Please upload an image where the width is 3/4 of the height."
@@ -2879,8 +2870,6 @@ msgid ""
"If you keep this empty, the ticket will be valid starting at the time of "
"purchase."
msgstr ""
"Si mantienes este campo vacío, la entrada será válida empezando en el "
"momento de la compra."
#: pretix/base/forms/questions.py:664 pretix/base/forms/questions.py:992
msgid "Street and Number"
@@ -2896,8 +2885,6 @@ msgid ""
"Optional, but depending on the country you reside in we might need to charge "
"you additional taxes if you do not enter it."
msgstr ""
"Opcional, pero dependiendo de tu país de residencia, es posible que haya que "
"aplicar cargos adicionales si no nos facilitas tu dirección."
#: pretix/base/forms/questions.py:1033 pretix/base/forms/questions.py:1039
msgid "If you are registered in Switzerland, you can enter your UID instead."
@@ -2908,8 +2895,6 @@ msgid ""
"Optional, but it might be required for you to claim tax benefits on your "
"invoice depending on your and the sellers country of residence."
msgstr ""
"Opcional, pero puede que sea necesario si aplican beneficios fiscales en tu "
"factura dependiendo del país de residencia del vendedor."
#: pretix/base/forms/questions.py:1129
msgid "You need to provide a company name."
@@ -2942,7 +2927,7 @@ msgstr "La contraseña actual que ingresó no es correcta."
#: pretix/base/forms/user.py:58
msgid "Please choose a password different to your current one."
msgstr "Elige una contraseña diferente a la actual."
msgstr ""
#: pretix/base/forms/user.py:63 pretix/presale/forms/customer.py:373
#: pretix/presale/forms/customer.py:442
@@ -3154,7 +3139,7 @@ msgstr "Monto"
#, python-brace-format
msgctxt "invoice"
msgid "Single price: {net_price} net / {gross_price} gross"
msgstr "Precio único: {net_price} neto / {gross_price} bruto"
msgstr ""
#: pretix/base/invoice.py:659
#, fuzzy, python-brace-format
@@ -3251,7 +3236,7 @@ msgstr "Por favor, seleccione una cuota."
#: pretix/base/media.py:61
msgid "Barcode / QR-Code"
msgstr "Código de barras / Código QR"
msgstr ""
#: pretix/base/media.py:77
#: pretix/control/templates/pretixcontrol/organizers/edit.html:237
@@ -3450,7 +3435,7 @@ msgstr "Tipo de ticket no está permitido"
#: pretix/base/models/checkin.py:351
msgid "Ticket code is ambiguous on list"
msgstr "El código de la entrada es ambiguo en la lista"
msgstr ""
#: pretix/base/models/checkin.py:352
#, fuzzy
@@ -3501,8 +3486,6 @@ msgid ""
"The identifier may only contain letters, numbers, dots, dashes, and "
"underscores. It must start and end with a letter or number."
msgstr ""
"El identificador solo puede contener letras, números, puntos, y barras "
"bajas. Tiene que empezar y terminar con una letra o un número."
#: pretix/base/models/customers.py:299 pretix/base/models/orders.py:1392
#: pretix/base/models/orders.py:2971 pretix/base/settings.py:1093
@@ -3518,7 +3501,7 @@ msgstr "Seleccione país"
#: pretix/base/models/customers.py:370
msgctxt "openidconnect"
msgid "Confidential"
msgstr "Confidencial"
msgstr ""
#: pretix/base/models/customers.py:371
#, fuzzy
@@ -3537,7 +3520,7 @@ msgstr "Código de transacción"
#: pretix/base/models/customers.py:378
msgctxt "openidconnect"
msgid "Implicit"
msgstr "Implícito"
msgstr ""
#: pretix/base/models/customers.py:382
msgid "OpenID Connect access (required)"
@@ -3589,7 +3572,7 @@ msgstr "Este identificador ya se utiliza para una pregunta diferente."
#: pretix/control/templates/pretixcontrol/organizers/gates.html:16
#: pretix/plugins/checkinlists/exporters.py:671
msgid "Gate"
msgstr "Puerta"
msgstr ""
#: pretix/base/models/devices.py:132
#: pretix/control/templates/pretixcontrol/organizers/devices.html:83
@@ -3981,7 +3964,7 @@ msgstr "Parametrizaciones adicionales"
#: pretix/base/models/exports.py:61 pretix/base/models/exports.py:66
#: pretix/base/models/exports.py:71
msgid "You can specify multiple recipients separated by commas."
msgstr "Puedes especificar múltiples destinatarios separados por comas."
msgstr ""
#: pretix/base/models/exports.py:64
#, fuzzy
@@ -4023,7 +4006,6 @@ msgstr "Hora de inicio del evento"
#: pretix/base/models/exports.py:86
msgid "The actual start time might be delayed depending on system load."
msgstr ""
"La hora de inicio real puede atrasarse dependiendo de la carga del sistema."
#: pretix/base/models/fields.py:33
msgid "No value can contain the delimiter character."
@@ -4435,11 +4417,11 @@ msgstr "minutos"
#: pretix/base/models/items.py:626
msgid "Hours"
msgstr "Horas"
msgstr ""
#: pretix/base/models/items.py:630
msgid "Days"
msgstr "Días"
msgstr ""
#: pretix/base/models/items.py:634
#, fuzzy
@@ -4471,7 +4453,7 @@ msgstr "El elemento seleccionado no pertenece a este evento."
#: pretix/base/models/items.py:650
msgid "Reusable media policy"
msgstr "Condiciones de utilización de imágenes"
msgstr ""
#: pretix/base/models/items.py:652
msgid ""
@@ -5160,7 +5142,7 @@ msgstr "Tarjeta de crédito"
#: pretix/base/models/memberships.py:44
msgid "Membership is transferable"
msgstr "La suscripción es transferible"
msgstr ""
#: pretix/base/models/memberships.py:45
msgid ""
@@ -5170,7 +5152,7 @@ msgstr ""
#: pretix/base/models/memberships.py:50
msgid "Parallel usage is allowed"
msgstr "El uso paralelo está permitido"
msgstr ""
#: pretix/base/models/memberships.py:51
msgid ""
@@ -5253,7 +5235,7 @@ msgstr ""
#: pretix/base/models/orders.py:234
msgid "We'll show you this order to be due for a follow-up on this day."
msgstr "Te mostraremos esta compra en el seguimiento de ese día."
msgstr ""
#: pretix/base/models/orders.py:240
msgid ""
@@ -5432,7 +5414,7 @@ msgstr "Tarifa de cancelación"
#: pretix/base/models/orders.py:2132
msgid "Insurance fee"
msgstr "Prima de seguro"
msgstr ""
#: pretix/base/models/orders.py:2133
msgid "Other fees"
@@ -5633,7 +5615,7 @@ msgstr ""
#: pretix/base/models/tax.py:168
msgid "Official name"
msgstr "Nombre oficial"
msgstr ""
#: pretix/base/models/tax.py:169
msgid "Should be short, e.g. \"VAT\""
@@ -5716,7 +5698,7 @@ msgstr ""
#: pretix/base/models/tax.py:372
msgctxt "invoice"
msgid "VAT liability rests with the service recipient."
msgstr "La responsabilidad del IVA es del destinatario del servicio."
msgstr ""
#: pretix/base/models/vouchers.py:171
msgid "No effect"
@@ -6003,7 +5985,7 @@ msgstr "Debe elegir el producto \"{prod}\" para este asiento."
#: pretix/base/models/vouchers.py:500
#, python-brace-format
msgid "The seat \"{id}\" is already sold or currently blocked."
msgstr "El puesto\"{id}\" ya se ha vendido o está bloqueado."
msgstr ""
#: pretix/base/models/waitinglist.py:64
msgid "On waiting list since"
@@ -6525,7 +6507,7 @@ msgstr "Habilitar método de pago"
#: pretix/base/payment.py:441
msgid "Share this link with customers who should use this payment method."
msgstr "Comparte este link con clientes que deben usar este método de pago."
msgstr ""
#: pretix/base/payment.py:487
msgctxt "invoice"
@@ -6966,7 +6948,7 @@ msgstr "Dirección de facturación empresa"
#: pretix/base/pdf.py:339
msgid "Sesame Street 42"
msgstr "Calle Sésamo 42"
msgstr ""
#: pretix/base/pdf.py:344
#, fuzzy
@@ -7349,8 +7331,6 @@ msgid ""
"All payments for this event need to be confirmed already, so no new orders "
"can be created."
msgstr ""
"Todos los pagos de este evento tienen que estar ya confirmados, por lo que "
"no se pueden crear nuevos pedidos."
#: pretix/base/services/cart.py:136
msgid ""
@@ -7599,12 +7579,12 @@ msgstr "Razón desconocida"
#: pretix/base/services/checkin.py:246
#, python-brace-format
msgid "Only allowed before {datetime}"
msgstr "Solo está permitido antes del {datetime}"
msgstr ""
#: pretix/base/services/checkin.py:248
#, python-brace-format
msgid "Only allowed after {datetime}"
msgstr "Solo está permitido después de {datetime}"
msgstr ""
#: pretix/base/services/checkin.py:251
msgid "Ticket type not allowed"
@@ -7673,22 +7653,22 @@ msgstr "Domingo"
#: pretix/base/services/checkin.py:307
#, python-brace-format
msgid "{variable} is not {value}"
msgstr "{variable} no es {value}"
msgstr ""
#: pretix/base/services/checkin.py:309
#, python-brace-format
msgid "Maximum {variable} exceeded"
msgstr "Máximo {variable} superado"
msgstr ""
#: pretix/base/services/checkin.py:311
#, python-brace-format
msgid "Minimum {variable} exceeded"
msgstr "Mínimo {variable} superado"
msgstr ""
#: pretix/base/services/checkin.py:313
#, python-brace-format
msgid "{variable} is {value}"
msgstr "{variable} es {value}"
msgstr ""
#: pretix/base/services/checkin.py:763
msgid "This order position has been canceled."
@@ -7851,7 +7831,7 @@ msgstr "Product de Ejemplo A"
#: pretix/base/services/invoices.py:519
#, python-brace-format
msgid "New invoice: {number}"
msgstr "Nueva factura: {number}"
msgstr ""
#: pretix/base/services/invoices.py:521
#, python-brace-format
@@ -7863,13 +7843,6 @@ msgid ""
"We are sending this email because you configured us to do so in your event "
"settings."
msgstr ""
"Hola,\n"
"\n"
"Una nueva factura para tu pedido {order} de {event} se ha creado, la puedes "
"encontrar adjunta.\n"
"\n"
"Te estamos enviando este email porque lo has configurado así en la página de "
"configuración del evento."
#: pretix/base/services/mail.py:266
#, fuzzy, python-brace-format
@@ -10078,7 +10051,17 @@ msgstr ""
"su equipo {event}"
#: pretix/base/settings.py:2108
#, python-brace-format
#, fuzzy, python-brace-format
#| msgid ""
#| "Hello,\n"
#| "\n"
#| "your order {code} for {event} has been canceled.\n"
#| "\n"
#| "You can view the details of your order at\n"
#| "{url}\n"
#| "\n"
#| "Best regards, \n"
#| "Your {event} team"
msgid ""
"Hello {attendee_name},\n"
"\n"
@@ -10090,15 +10073,15 @@ msgid ""
"Best regards, \n"
"Your {event} team"
msgstr ""
"Hola, {attendee_name}: \n"
"Hola, \n"
"\n"
"Te has registrado correctamente para {event}. \n"
"su pedido {code} para {event} ha sido cancelado. \n"
"\n"
"Puedes ver el estado y los detalles de tu entrada aquí:\n"
"Puede ver los detalles de su pedido en\n"
"{url}. \n"
"\n"
"Saludos cordiales, \n"
"El equipo {event}"
"su equipo {event}"
#: pretix/base/settings.py:2128
#, python-brace-format
@@ -10638,7 +10621,19 @@ msgid "Order approved and confirmed: {code}"
msgstr "Pedido aprobado y confirmado: {code}"
#: pretix/base/settings.py:2462
#, python-brace-format
#, fuzzy, python-brace-format
#| msgid ""
#| "Hello,\n"
#| "\n"
#| "your order for {event} was successful. As you only ordered free "
#| "products,\n"
#| "no payment is required.\n"
#| "\n"
#| "You can change your order details and view the status of your order at\n"
#| "{url}\n"
#| "\n"
#| "Best regards, \n"
#| "Your {event} team"
msgid ""
"Hello,\n"
"\n"
@@ -10653,15 +10648,15 @@ msgid ""
msgstr ""
"Hola, \n"
"\n"
"se ha aprobado tu pedido para {event} y te damos la bienvenida a nuestro "
"evento. Como solo ha pedido productos gratuitos, no se requiere ningún pago. "
"\n"
"su pedido para {event} fue un exitoso. Como sólo ha pedido productos "
"gratuitos , \n"
"no se requiere ningún pago. \n"
"\n"
"Puede cambiar los detalles de su pedido y ver el estado del mismo en\n"
"{url}\n"
"\n"
"Saludos cordiales, \n"
"El equipo de {event}"
"su equipo {event}"
#: pretix/base/settings.py:2495
#, python-brace-format
@@ -10722,7 +10717,17 @@ msgid "Your ticket is ready for download: {code}"
msgstr "Su ticket está listo para descargar: {code}"
#: pretix/base/settings.py:2536
#, python-brace-format
#, fuzzy, python-brace-format
#| msgid ""
#| "Hello {attendee_name},\n"
#| "\n"
#| "you are registered for {event}.\n"
#| "\n"
#| "If you did not do so already, you can download your ticket here:\n"
#| "{url}\n"
#| "\n"
#| "Best regards, \n"
#| "Your {event} team"
msgid ""
"Hello {attendee_name},\n"
"\n"
@@ -10734,11 +10739,11 @@ msgid ""
"Best regards, \n"
"Your {event} team"
msgstr ""
"Hola, {attendee_name}:\n"
"Hola {attendee_name},\n"
"\n"
"está registrado en {event}.\n"
"\n"
"Si aún no lo ha hecho, puede descargar su entrada aquí:\n"
"Si aún no lo ha hecho, puede descargar su entrada aquí :\n"
"{url} \n"
"\n"
"Saludos cordiales, \n"
@@ -12448,8 +12453,10 @@ msgid "Ask for {fields}, display like {example}"
msgstr "Pregunta por {fields}, despliega como {example}"
#: pretix/control/forms/event.py:634 pretix/control/forms/organizer.py:454
#, fuzzy
#| msgid "Free price input"
msgid "Free text input"
msgstr "Entrada de texto libre"
msgstr "Entrada de precio gratuita"
#: pretix/control/forms/event.py:666
#, fuzzy
@@ -12726,8 +12733,10 @@ msgid "Subject for approved free order"
msgstr "Pedido aprobado"
#: pretix/control/forms/event.py:1233
#, fuzzy
#| msgid "Approved order"
msgid "Text for approved free order"
msgstr "Texto aprobado"
msgstr "Pedido aprobado"
#: pretix/control/forms/event.py:1236 pretix/control/forms/event.py:1254
#, fuzzy
@@ -14250,7 +14259,7 @@ msgstr "ticket secreto:"
#: pretix/control/forms/orders.py:458
msgid "Validity start"
msgstr "Incio de validez"
msgstr ""
#: pretix/control/forms/orders.py:463
#, fuzzy
@@ -14401,8 +14410,10 @@ msgid "Keep a fixed cancellation fee per ticket"
msgstr "Mantener una tarifa de cancelación fija"
#: pretix/control/forms/orders.py:828
#, fuzzy
#| msgid "Generate tickets for non-admission products"
msgid "Free tickets and add-on products are not counted"
msgstr "Las entradas gratuitas en productos complementarios no contabilizan"
msgstr "Generar tickets para productos no admitidos"
#: pretix/control/forms/orders.py:838
#, fuzzy
@@ -14596,23 +14607,29 @@ msgid "Gift card value"
msgstr "Tarjeta de regalo"
#: pretix/control/forms/organizer.py:700
#, fuzzy
#| msgid "This ticket has already been redeemed."
msgid "An medium with this type and identifier is already registered."
msgstr "Un medio con este tipo y este identificador ya ha sido registrado."
msgstr "Este ticket ya ha sido canjeado."
#: pretix/control/forms/organizer.py:801
#, fuzzy
#| msgid "This ticket has already been redeemed."
msgid "An account with this customer ID is already registered."
msgstr "Una cuenta con este identificador de usuario ya está registrado."
msgstr "Este ticket ya ha sido canjeado."
#: pretix/control/forms/organizer.py:802 pretix/presale/forms/customer.py:439
#, fuzzy
#| msgid "This ticket has already been redeemed."
msgid "An account with this email address is already registered."
msgstr "Una cuenta con esta dirección de correo ya está registrada."
msgstr "Este ticket ya ha sido canjeado."
#: pretix/control/forms/organizer.py:818
#: pretix/control/templates/pretixcontrol/organizers/customer.html:60
#: pretix/presale/forms/customer.py:156 pretix/presale/forms/customer.py:472
#: pretix/presale/templates/pretixpresale/organizers/customer_profile.html:32
msgid "Phone"
msgstr "Teléfono"
msgstr ""
#: pretix/control/forms/organizer.py:925
#, fuzzy
@@ -14638,7 +14655,7 @@ msgstr "Clave Secreta"
#: pretix/control/forms/organizer.py:937
msgctxt "sso_oidc"
msgid "Scope"
msgstr "Alcance"
msgstr ""
#: pretix/control/forms/organizer.py:938
msgctxt "sso_oidc"
@@ -14648,7 +14665,7 @@ msgstr ""
#: pretix/control/forms/organizer.py:942
msgctxt "sso_oidc"
msgid "User ID field"
msgstr "Campo identificador del usuario"
msgstr ""
#: pretix/control/forms/organizer.py:943
msgctxt "sso_oidc"
@@ -17047,8 +17064,6 @@ msgid ""
"We've detected that you are using <strong>Microsoft Internet Explorer</"
"strong>."
msgstr ""
"Hemos detectado que estás usando <strong> Microsoft Internet Explorer "
"</strong>."
#: pretix/control/templates/pretixcontrol/base.html:328
msgid ""
@@ -17126,8 +17141,6 @@ msgid ""
"For security reasons, please change your password before you continue. "
"Afterwards you will be redirected to your original destination."
msgstr ""
"Por motivos de seguridad, por favor cambia tu contraseña antes de continuar. "
"Serás redirigido después a la página de origen."
#: pretix/control/templates/pretixcontrol/base.html:446
#, python-format
@@ -17316,7 +17329,7 @@ msgstr "Resultado"
#: pretix/control/templates/pretixcontrol/boxoffice/payment.html:108
msgid "Cash"
msgstr "Efectivo"
msgstr ""
#: pretix/control/templates/pretixcontrol/checkin/checkins.html:9
#: pretix/control/templates/pretixcontrol/checkin/checkins.html:41
@@ -20826,7 +20839,7 @@ msgstr "No hay solicitudes registradas todavía."
#: pretix/control/templates/pretixcontrol/oauth/app_register.html:4
#: pretix/control/templates/pretixcontrol/oauth/app_register.html:6
msgid "Register a new application"
msgstr "Hacer un nuevo registro"
msgstr "Registrar una nueva aplicación"
#: pretix/control/templates/pretixcontrol/oauth/app_rollkeys.html:4
#: pretix/control/templates/pretixcontrol/oauth/app_rollkeys.html:6
@@ -22693,7 +22706,7 @@ msgstr "Crear varias fechas"
#: pretix/control/templates/pretixcontrol/subevents/bulk_edit.html:13
#, python-format
msgid "%(number)s selected"
msgstr "%(number)s selecionado"
msgstr ""
#: pretix/control/templates/pretixcontrol/organizers/device_bulk_edit.html:36
#: pretix/control/templates/pretixcontrol/organizers/device_edit.html:24
@@ -26915,8 +26928,10 @@ msgid "The device has been removed."
msgstr "El dispositivo ha sido retirado."
#: pretix/control/views/user.py:449
#, fuzzy
#| msgid "This ticket has already been redeemed."
msgid "This security device is already registered."
msgstr "Este dispositivo ya está registrado."
msgstr "Este ticket ya ha sido canjeado."
#: pretix/control/views/user.py:471 pretix/control/views/user.py:532
msgid "A new two-factor authentication device has been added to your account."
@@ -28800,8 +28815,6 @@ msgid ""
"After placing your order, you will be able to select your desired payment "
"method, including PayPal."
msgstr ""
"Después de hacer el pedido, podrás elegir el método de pago preferido, "
"incluyendo PayPal."
#: pretix/plugins/paypal2/templates/pretixplugins/paypal2/checkout_payment_form.html:5
msgid ""
@@ -29830,8 +29843,10 @@ msgid "Blocked Seats"
msgstr ""
#: pretix/plugins/statistics/templates/pretixplugins/statistics/index.html:86
#, fuzzy
#| msgid "Free order"
msgid "Free Seats"
msgstr "Asiento gratuito"
msgstr "Pedido gratuito"
#: pretix/plugins/statistics/templates/pretixplugins/statistics/index.html:94
#, fuzzy
@@ -31051,8 +31066,6 @@ msgid ""
"An account with this email address is already registered. Please try to log "
"in or reset your password instead."
msgstr ""
"Una cuenta con esta dirección de correo ya está registrada. Puedes entrar o "
"recuperar la contraseña."
#: pretix/presale/forms/customer.py:189
#, python-brace-format
@@ -31694,7 +31707,7 @@ msgstr "cantidad mínima a pedir: %(num)s"
#: pretix/presale/templates/pretixpresale/event/voucher.html:354
msgctxt "price"
msgid "free"
msgstr "gratis"
msgstr ""
#: pretix/presale/templates/pretixpresale/event/fragment_addon_choice.html:77
#: pretix/presale/templates/pretixpresale/event/fragment_product_list.html:51
@@ -32387,7 +32400,7 @@ msgstr ""
#: pretix/presale/templates/pretixpresale/event/fragment_subevent_calendar.html:22
#: pretix/presale/templates/pretixpresale/organizers/calendar.html:24
msgid "Select month to show"
msgstr "Seleccione un mes a mostrar"
msgstr ""
#: pretix/presale/templates/pretixpresale/event/fragment_subevent_calendar.html:32
#: pretix/presale/templates/pretixpresale/event/fragment_subevent_calendar_week.html:36
@@ -32410,7 +32423,7 @@ msgstr ""
#: pretix/presale/templates/pretixpresale/event/fragment_subevent_calendar_week.html:22
#: pretix/presale/templates/pretixpresale/organizers/calendar_week.html:26
msgid "Select week to show"
msgstr "Selecciona semana a mostrar"
msgstr ""
#: pretix/presale/templates/pretixpresale/event/fragment_subevent_calendar_week.html:43
#, python-format
@@ -32766,7 +32779,6 @@ msgstr "Cambiar detalles"
msgid ""
"You need to select a payment method above before you can request an invoice."
msgstr ""
"Es necesario seleccionar un método de pago antes de solicitar una factura."
#: pretix/presale/templates/pretixpresale/event/order.html:267
#: pretix/presale/templates/pretixpresale/event/order.html:274
@@ -33129,8 +33141,6 @@ msgid ""
"Please select the desired changes to your ticket. Note that you can only "
"perform changes that do not change the total price of the ticket."
msgstr ""
"Selecciona los cambios que quieres hacer en tu entrada. Ten en cuenta que "
"solo puedes hacer cambios que no cambien el valor total de la entrada."
#: pretix/presale/templates/pretixpresale/event/position_change_confirm.html:19
#, fuzzy
@@ -33692,7 +33702,7 @@ msgstr "No se ha encontrado el organizador seleccionado."
msgid ""
"Your selected payment method can only be used for a payment of at least "
"{amount}."
msgstr "El método de pago solo se puede usar para un pago de mínimo {amount}."
msgstr ""
#: pretix/presale/views/cart.py:183
msgid "Please enter positive numbers only."
@@ -33971,8 +33981,6 @@ msgid ""
"Thank you very much! We will assign your spot on the waiting list to someone "
"else."
msgstr ""
"¡Muchas gracias! Le asignaremos tu puesto en la lista de espera a otra "
"persona."
#: pretix/presale/views/widget.py:341
#, fuzzy
@@ -33993,7 +34001,7 @@ msgstr "de %(start_date)s"
#: pretix/settings.py:710
msgid "User profile only"
msgstr "Solo perfil de usuario"
msgstr ""
#: pretix/settings.py:711
msgid "Read access"

View File

@@ -4,7 +4,7 @@ msgstr ""
"Project-Id-Version: 1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-07-27 11:49+0000\n"
"PO-Revision-Date: 2023-08-09 03:00+0000\n"
"PO-Revision-Date: 2023-07-22 21:00+0000\n"
"Last-Translator: Ronan LE MEILLAT <ronan.le_meillat@highcanfly.club>\n"
"Language-Team: French <https://translate.pretix.eu/projects/pretix/pretix/fr/"
">\n"
@@ -13,7 +13,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 4.18.2\n"
"X-Generator: Weblate 4.17\n"
#: pretix/_base_settings.py:78
msgid "English"
@@ -1666,7 +1666,7 @@ msgstr "Nécessite une attention particulière"
#: pretix/base/exporters/items.py:91 pretix/base/models/items.py:553
#: pretix/base/models/items.py:1018
msgid "Original price"
msgstr "Prix d'origine"
msgstr "Facture originale"
#: pretix/base/exporters/items.py:92 pretix/base/models/items.py:565
msgid "This product is a gift card"
@@ -6227,18 +6227,24 @@ msgid "Ambiguous option selected."
msgstr "Option ambiguë sélectionnée."
#: pretix/base/orderimport.py:845
#, fuzzy
#| msgid "No matching seat was found."
msgid "No matching customer was found."
msgstr "Aucun client correspondant na été trouvé."
msgstr "Aucun siège correspondant na été trouvé."
#: pretix/base/payment.py:86
#, fuzzy
#| msgid "Apply"
msgctxt "payment"
msgid "Apple Pay"
msgstr "Apple Pay"
msgstr "Appliquer"
#: pretix/base/payment.py:87
#, fuzzy
#| msgid "Android (Google Play)"
msgctxt "payment"
msgid "Google Pay"
msgstr "Google Pay"
msgstr "Android (Google Play)"
#: pretix/base/payment.py:256
#: pretix/presale/templates/pretixpresale/event/order.html:115
@@ -8223,14 +8229,16 @@ msgid "Gift card currency"
msgstr "Devise de la carte-cadeau"
#: pretix/base/settings.py:277
#, fuzzy
#| msgid ""
#| "Automatically create a new gift card if a previously unknown chip is seen"
msgid "Automatically create a new gift card if a new chip is encoded"
msgstr ""
"Créer automatiquement une nouvelle carte-cadeau si une nouvelle puce est "
"encodée"
"Créer automatiquement une nouvelle carte-cadeau si une puce inconnue est vue"
#: pretix/base/settings.py:299
msgid "Use UID protection feature of NFC chip"
msgstr "Utiliser la fonction de protection UID de la puce NFC"
msgstr ""
#: pretix/base/settings.py:313
msgid "Maximum number of items per order"
@@ -8745,8 +8753,10 @@ msgstr ""
"commandés par d'autres personnes."
#: pretix/base/settings.py:942
#, fuzzy
#| msgid "Expiration date"
msgid "Expiration delay"
msgstr "Délai dexpiration"
msgstr "Date d'expiration"
#: pretix/base/settings.py:943
msgid ""
@@ -8756,11 +8766,6 @@ msgid ""
"beyond the \"last date of payments\" configured above, which is always "
"enforced."
msgstr ""
"La commande nexpirera réellement que ce nombre de jours après la date d"
"expiration communiquée au client. Si vous sélectionnez « Ne terminez les "
"conditions de paiement que les jours de semaine » ci-dessus, cela sera "
"également respecté. Cependant, cela ne retardera pas au-delà de la « "
"dernière date de paiement » configurée ci-dessus, qui est toujours appliquée."
#: pretix/base/settings.py:964
msgid "Hide \"payment pending\" state on customer-facing pages"
@@ -10181,12 +10186,25 @@ msgstr ""
"Votre équipe {event}"
#: pretix/base/settings.py:2349
#, python-brace-format
#, fuzzy, python-brace-format
#| msgid "Payment received for your order: {code}"
msgid "Payment failed for your order: {code}"
msgstr "Paiement échoué pour votre commande: {code}"
msgstr "Paiement reçu pour votre commande: {code}"
#: pretix/base/settings.py:2353
#, python-brace-format
#, fuzzy, python-brace-format
#| msgid ""
#| "Hello,\n"
#| "\n"
#| "we did not yet receive a full payment for your order for {event}.\n"
#| "Please keep in mind that we only guarantee your order if we receive\n"
#| "your payment before {expire_date}.\n"
#| "\n"
#| "You can view the payment information and the status of your order at\n"
#| "{url}\n"
#| "\n"
#| "Best regards, \n"
#| "Your {event} team"
msgid ""
"Hello,\n"
"\n"
@@ -10204,18 +10222,18 @@ msgid ""
msgstr ""
"Bonjour\n"
"\n"
"Votre tentative de paiement pour votre commande pour {event} a échoué.\n"
"Nous navons pas encore reçu le paiement intégral de votre commande de "
"{event}.\n"
"Veuillez garder à lesprit que nous ne garantissons votre commande que si "
"nous recevons\n"
"votre paiement avant {expire_date}.\n"
"\n"
"Votre commande est toujours valide et vous pouvez essayer de payer à nouveau "
"en utilisant le même mode de paiement ou un mode de paiement différent. "
"Veuillez effectuer votre paiement avant {expire_date}.\n"
"\n"
"Vous pouvez réessayer le paiement et consulter létat de votre commande à l"
"adresse\n"
"Vous pouvez consulter les informations de paiement et létat de votre "
"commande à ladresse\n"
"{url}\n"
"\n"
"Sinceres salutations \n"
"Votre équipe {event}"
"Votre {event} équipe"
#: pretix/base/settings.py:2367
#, python-brace-format
@@ -11177,7 +11195,7 @@ msgstr ""
#: pretix/base/settings.py:3720
msgid "This needs to be disabled if other NFC-based types are active."
msgstr "Cela doit être désactivé si dautres types NFC sont actifs."
msgstr ""
#: pretix/base/shredder.py:74 pretix/base/shredder.py:77
msgid "Your event needs to be over to use this feature."
@@ -14902,8 +14920,10 @@ msgid "The medium has been connected to a new ticket."
msgstr "Le média a été connecté à un nouveau ticket."
#: pretix/control/logdisplay.py:371
#, fuzzy
#| msgid "The medium has been connected to a new ticket."
msgid "The medium has been connected to a new gift card."
msgstr "Le média a été connecté à une nouvelle carte cadeau."
msgstr "Le média a été connecté à un nouveau ticket."
#: pretix/control/logdisplay.py:372 pretix/control/logdisplay.py:413
msgid "Sending of an email has failed."
@@ -15173,9 +15193,12 @@ msgstr ""
"l'utilisateur."
#: pretix/control/logdisplay.py:436
#, fuzzy
#| msgid ""
#| "An email has been sent to notify the user that payment has been received."
msgid "An email has been sent to notify the user that the payment failed."
msgstr ""
"Un mail a été envoyé pour informer l'utilisateur que le paiement a échoué."
"Un mail a été envoyé pour informer l'utilisateur que le paiement a été reçu."
#: pretix/control/logdisplay.py:437
#, python-brace-format
@@ -18000,7 +18023,7 @@ msgstr "Contenu de l' e-mail"
#: pretix/control/templates/pretixcontrol/event/mail.html:90
msgid "Placed order"
msgstr "Commande placée"
msgstr "Ordre placé"
#: pretix/control/templates/pretixcontrol/event/mail.html:93
msgid "Paid order"
@@ -18021,8 +18044,10 @@ msgid "Payment reminder"
msgstr "Rappel de paiement"
#: pretix/control/templates/pretixcontrol/event/mail.html:108
#, fuzzy
#| msgid "Payment fee"
msgid "Payment failed"
msgstr "Paiement échoué"
msgstr "Frais de paiement"
#: pretix/control/templates/pretixcontrol/event/mail.html:111
msgid "Waiting list notification"
@@ -18077,6 +18102,8 @@ msgid "Deadlines"
msgstr "Échéances"
#: pretix/control/templates/pretixcontrol/event/payment.html:68
#, fuzzy
#| msgid "days"
msgctxt "unit"
msgid "days"
msgstr "jours"
@@ -22149,10 +22176,6 @@ msgid ""
"made by NXP. This provides a higher level of security than other approaches, "
"but requires all chips to be encoded prior to use."
msgstr ""
"Ce type de support ne fonctionne quavec des puces NFC du type Mifare "
"Ultralight AES fabriquées par NXP. Cela fournit un niveau de sécurité plus "
"élevé que les autres approches, mais nécessite que toutes les puces soient "
"encodées avant utilisation."
#: pretix/control/templates/pretixcontrol/organizers/export.html:64
msgid "Run export now and download result"
@@ -24232,15 +24255,19 @@ msgid ""
"For safety reasons, the waiting list does not run if the quota is set to "
"unlimited."
msgstr ""
"Pour des raisons de sécurité, la liste dattente ne fonctionne pas si le "
"quota est fixé sur illimité."
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:219
#, fuzzy
#| msgid "Quota name"
msgid "Quota unlimited"
msgstr "Quota illimité"
msgstr "Nom du quota"
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:225
#, python-format
#, fuzzy, python-format
#| msgid ""
#| "\n"
#| " Waiting, product %(num)sx available\n"
#| " "
msgid ""
"\n"
" Waiting, product %(num)sx "
@@ -24248,9 +24275,8 @@ msgid ""
" "
msgstr ""
"\n"
" En attente, produit %(num)sx "
"disponible\n"
" "
" En attente, produit %(num)sx disponible\n"
" "
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:231
msgid "Waiting, product unavailable"
@@ -25021,6 +25047,13 @@ msgid "The selected product has been deactivated."
msgstr "Le produit sélectionné a été désactivé."
#: pretix/control/views/mailsetup.py:195
#, fuzzy
#| msgid ""
#| "We could not find an SPF record set for the domain you are trying to use. "
#| "You can still proceed, but it will increase the chance of emails going to "
#| "spam or being rejected. We strongly recommend setting an SPF record on "
#| "the domain. You can do so through the DNS settings at the provider you "
#| "registered your domain with."
msgid ""
"We could not find an SPF record set for the domain you are trying to use. "
"This means that there is a very high change most of the emails will be "
@@ -25029,11 +25062,11 @@ msgid ""
"registered your domain with."
msgstr ""
"Nous navons pas pu trouver de jeu denregistrements SPF pour le domaine que "
"vous essayez dutiliser. Cela signifie quil y a un changement très élevé, "
"la plupart des e-mails seront rejetés ou marqués comme spam. Nous vous "
"recommandons vivement de définir un enregistrement SPF sur le domaine. Vous "
"pouvez le faire via les paramètres DNS du fournisseur auprès duquel vous "
"avez enregistré votre domaine."
"vous essayez dutiliser. Vous pouvez toujours continuer, mais cela "
"augmentera les chances que les e-mails soient envoyés au spam ou rejetés. "
"Nous vous recommandons vivement de définir un enregistrement SPF sur le "
"domaine. Vous pouvez le faire via les paramètres DNS du fournisseur auprès "
"duquel vous avez enregistré votre domaine."
#: pretix/control/views/mailsetup.py:202
msgid ""
@@ -28127,6 +28160,9 @@ msgid "Restrict to event dates starting before"
msgstr "Limiter aux dates dévénements commençant avant"
#: pretix/plugins/sendmail/forms.py:170
#, fuzzy
#| msgctxt "sendmail_from"
#| msgid "Send to"
msgctxt "sendmail_form"
msgid "Send to"
msgstr "Envoyer à"
@@ -28141,6 +28177,9 @@ msgid "Filter check-in status"
msgstr "Filtrer le statut d'enregistrement"
#: pretix/plugins/sendmail/forms.py:189
#, fuzzy
#| msgctxt "sendmail_from"
#| msgid "Restrict to recipients without check-in"
msgctxt "sendmail_form"
msgid "Restrict to recipients without check-in"
msgstr "Restreindre aux destinataires sans enregistrement"
@@ -28194,11 +28233,17 @@ msgid "pending with payment overdue"
msgstr "en attente avec retard"
#: pretix/plugins/sendmail/forms.py:258
#, fuzzy
#| msgctxt "sendmail_from"
#| msgid "Restrict to orders with status"
msgctxt "sendmail_form"
msgid "Restrict to orders with status"
msgstr "Restreindre aux commandes avec statut"
#: pretix/plugins/sendmail/forms.py:283 pretix/plugins/sendmail/forms.py:287
#, fuzzy
#| msgctxt "sendmail_from"
#| msgid "Restrict to recipients with check-in on list"
msgctxt "sendmail_form"
msgid "Restrict to recipients with check-in on list"
msgstr "Restreindre aux destinataires avec enregistrement sur la liste"
@@ -28269,6 +28314,9 @@ msgid "Limit products"
msgstr "Limiter les produits"
#: pretix/plugins/sendmail/models.py:218
#, fuzzy
#| msgctxt "sendmail_from"
#| msgid "Restrict to orders with status"
msgid "Restrict to orders with status"
msgstr "Restreindre aux commandes avec statut"
@@ -28832,7 +28880,7 @@ msgstr ""
#: pretix/plugins/stripe/payment.py:296
msgid "Check for Apple Pay/Google Pay"
msgstr "Vérifier Apple Pay/Google Pay"
msgstr ""
#: pretix/plugins/stripe/payment.py:300
msgid ""
@@ -28842,11 +28890,6 @@ msgid ""
"take into consideration if Google Pay/Apple Pay has been disabled in the "
"Stripe Dashboard."
msgstr ""
"pretix tentera de vérifier si le navigateur Web du client prend en charge "
"les méthodes de paiement basées sur le portefeuille comme Apple Pay ou "
"Google Pay et les affichera bien en évidence avec le mode de paiement par "
"carte de crédit. Cette détection ne prend pas en compte si Google Pay/Apple "
"Pay a été désactivé dans le tableau de bord Stripe."
#: pretix/plugins/stripe/payment.py:309
msgid "Statement descriptor postfix"
@@ -28896,32 +28939,38 @@ msgid "Bancontact"
msgstr "Bancontact"
#: pretix/plugins/stripe/payment.py:357
#, fuzzy
#| msgid "Disable SEPA Direct Debit"
msgid "SEPA Direct Debit"
msgstr "Prélèvement SEPA"
msgstr "Désactiver le prélèvement SEPA"
#: pretix/plugins/stripe/payment.py:362
#, fuzzy
#| msgid ""
#| "Despite the name, Sofort payments via Stripe are <strong>not</strong> "
#| "processed instantly but might take up to <strong>14 days</strong> to be "
#| "confirmed in some cases. Please only activate this payment method if your "
#| "payment term allows for this lag."
msgid ""
"SEPA Direct Debit payments via Stripe are <strong>not</strong> processed "
"instantly but might take up to <strong>14 days</strong> to be confirmed in "
"some cases. Please only activate this payment method if your payment term "
"allows for this lag."
msgstr ""
"Les paiements par prélèvement SEPA via Stripe ne sont <strong>pas</strong> "
"traités instantanément, mais peuvent prendre jusquà <strong>14 jours</"
"strong> pour être confirmés dans certains cas. Veuillez nactiver ce mode de "
"paiement que si votre délai de paiement le permet."
"Malgré leur nom, les paiements Sofort via Stripe <strong>ne sont pas</"
"strong> traités instantanément, mais peuvent prendre jusquà <strong>14 "
"jours</strong> pour être confirmés dans certains cas. Veuillez nactiver ce "
"mode de paiement que si votre délai de paiement le permet."
#: pretix/plugins/stripe/payment.py:370
msgid "SEPA Creditor Mandate Name"
msgstr "Nom du mandat du créancier SEPA"
msgstr ""
#: pretix/plugins/stripe/payment.py:372
msgid ""
"Please provide your SEPA Creditor Mandate Name, that will be displayed to "
"the user."
msgstr ""
"Veuillez fournir votre nom de mandat de créancier SEPA, qui sera affiché à "
"lutilisateur."
#: pretix/plugins/stripe/payment.py:383
msgid "SOFORT"
@@ -29032,32 +29081,44 @@ msgid "Credit card"
msgstr "Carte de crédit"
#: pretix/plugins/stripe/payment.py:1157
#, fuzzy
#| msgid "EPS via Stripe"
msgid "SEPA Debit via Stripe"
msgstr "Prélèvement SEPA via Stripe"
msgstr "EPS via Stripe"
#: pretix/plugins/stripe/payment.py:1158
msgid "SEPA Debit"
msgstr "Débit SEPA"
msgstr ""
#: pretix/plugins/stripe/payment.py:1197
#, fuzzy
#| msgid "Account holder"
msgid "Account Holder Name"
msgstr "Titulaire du compte"
#: pretix/plugins/stripe/payment.py:1202
#, fuzzy
#| msgid "Account holder"
msgid "Account Holder Street"
msgstr "Rue du titulaire du compte"
msgstr "Titulaire du compte"
#: pretix/plugins/stripe/payment.py:1214
#, fuzzy
#| msgid "Account holder"
msgid "Account Holder Postal Code"
msgstr "Code postal du titulaire du compte"
msgstr "Titulaire du compte"
#: pretix/plugins/stripe/payment.py:1226
#, fuzzy
#| msgid "Account holder"
msgid "Account Holder City"
msgstr "Ville du titulaire du compte"
msgstr "Titulaire du compte"
#: pretix/plugins/stripe/payment.py:1238
#, fuzzy
#| msgid "Account holder"
msgid "Account Holder Country"
msgstr "Pays du titulaire du compte"
msgstr "Titulaire du compte"
#: pretix/plugins/stripe/payment.py:1282
msgid "giropay via Stripe"
@@ -29228,18 +29289,22 @@ msgid "Card type"
msgstr "Type de carte"
#: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_confirm.html:14
#, fuzzy
#| msgid "The total amount will be withdrawn from your credit card."
msgid "The total amount will be withdrawn from your bank account."
msgstr "Le montant total sera prélevé sur votre compte bancaire."
msgstr "Le montant total sera prélevé sur votre carte de crédit."
#: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_confirm.html:18
#: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_sepadirectdebit.html:20
msgid "Banking Institution"
msgstr "Établissement bancaire"
msgstr ""
#: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_confirm.html:20
#: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_sepadirectdebit.html:22
#, fuzzy
#| msgid "Account holder"
msgid "Account number"
msgstr "Numéro de compte"
msgstr "Titulaire du compte"
#: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_confirm.html:24
#: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_simple.html:4
@@ -29288,20 +29353,28 @@ msgstr ""
"serveurs."
#: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_sepadirectdebit.html:10
#, fuzzy
#| msgid "For a credit card payment, please turn on JavaScript."
msgid "For a SEPA Debit payment, please turn on JavaScript."
msgstr "Pour un paiement par prélèvement SEPA, veuillez activer JavaScript."
msgstr "Pour un paiement par carte de crédit, veuillez activer JavaScript."
#: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_sepadirectdebit.html:16
#, fuzzy
#| msgid ""
#| "You already entered a card number that we will use to charge the payment "
#| "amount."
msgid ""
"You already entered a bank account that we will use to charge the payment "
"amount."
msgstr ""
"Vous avez déjà saisi un compte bancaire que nous utiliserons pour débiter le "
"Vous avez déjà entré un numéro de carte que nous utiliserons pour débiter le "
"montant du paiement."
#: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_sepadirectdebit.html:27
#, fuzzy
#| msgid "Use a different card"
msgid "Use a different account"
msgstr "Utiliser un autre compte"
msgstr "Utiliser une autre carte"
#: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_sepadirectdebit.html:51
#, python-format
@@ -29317,18 +29390,6 @@ msgid ""
"statement that you can obtain from your bank. You agree to receive "
"notifications for future debits up to 2 days before they occur."
msgstr ""
"En fournissant vos informations de paiement et en confirmant ce paiement, "
"vous autorisez (A) %(sepa_creditor_name)s et Stripe, notre prestataire de "
"services de paiement et/ou PPRO, son prestataire de services local, à "
"envoyer des instructions à votre banque pour débiter votre compte et (B) "
"votre banque à débiter votre compte conformément à ces instructions. Dans le "
"cadre de vos droits, vous avez droit à un remboursement de votre banque "
"selon les termes et conditions de votre accord avec votre banque. Un "
"remboursement doit être demandé dans un délai de 8 semaines à compter de la "
"date à laquelle votre compte a été débité. Vos droits sont expliqués dans "
"une déclaration que vous pouvez obtenir auprès de votre banque. Vous "
"acceptez de recevoir des notifications pour les débits futurs jusquà 2 "
"jours avant quils ne se produisent."
#: pretix/plugins/stripe/templates/pretixplugins/stripe/control.html:6
msgid "Charge ID"

View File

@@ -7,7 +7,7 @@ msgstr ""
"Project-Id-Version: French\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-07-21 11:46+0000\n"
"PO-Revision-Date: 2023-08-02 02:00+0000\n"
"PO-Revision-Date: 2023-07-19 17:00+0000\n"
"Last-Translator: Ronan LE MEILLAT <ronan.le_meillat@highcanfly.club>\n"
"Language-Team: French <https://translate.pretix.eu/projects/pretix/pretix-js/"
"fr/>\n"
@@ -16,7 +16,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 4.18.2\n"
"X-Generator: Weblate 4.17\n"
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
@@ -63,7 +63,7 @@ msgstr "iDEAL"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:42
msgid "SEPA Direct Debit"
msgstr "Prélèvement SEPA"
msgstr "Débit direct SEPA"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:43
msgid "Bancontact"
@@ -679,8 +679,10 @@ msgid "Your local time:"
msgstr "Votre heure locale:"
#: pretix/static/pretixpresale/js/walletdetection.js:39
#, fuzzy
#| msgid "Apple Pay"
msgid "Google Pay"
msgstr "Google Pay"
msgstr "Apple Pay"
#: pretix/static/pretixpresale/js/widget/widget.js:17
msgctxt "widget"

View File

@@ -77,8 +77,6 @@ def get_event_domain(event, fallback=False, return_info=False):
def get_organizer_domain(organizer):
assert isinstance(organizer, Organizer)
if not organizer.pk:
return None
domain = getattr(organizer, '_cached_domain', None) or organizer.cache.get('domain')
if domain is None:
domains = organizer.domains.filter(event__isnull=True)
@@ -128,7 +126,7 @@ def eventreverse(obj, name, kwargs=None):
:param kwargs: A dictionary of additional keyword arguments that should be used. You do not
need to provide the organizer or event slug here, it will be added automatically as
needed.
:returns: An absolute or relative URL as a string
:returns: An absolute URL (including scheme and host) as a string
"""
from pretix.multidomain import (
event_domain_urlconf, maindomain_urlconf, organizer_domain_urlconf,
@@ -177,17 +175,6 @@ def eventreverse(obj, name, kwargs=None):
def build_absolute_uri(obj, urlname, kwargs=None):
"""
Works similar to ``eventreverse`` but always returns an absolute URL.
:param obj: An ``Event`` or ``Organizer`` object
:param name: The name of the URL route
:type name: str
:param kwargs: A dictionary of additional keyword arguments that should be used. You do not
need to provide the organizer or event slug here, it will be added automatically as
needed.
:returns: An absolute URL (including scheme and host) as a string
"""
reversedurl = eventreverse(obj, urlname, kwargs)
if '://' in reversedurl:
return reversedurl

View File

@@ -535,11 +535,9 @@ class BankTransfer(BasePaymentProvider):
'eu_barcodes': self.event.currency == 'EUR',
'pending_description': self.settings.get('pending_description', as_type=LazyI18nString),
'details': self.settings.get('bank_details', as_type=LazyI18nString),
'has_invoices': payment.order.invoices.exists(),
'invoice_email_enabled': self.settings.get('invoice_email', as_type=bool),
}
ctx['any_barcodes'] = ctx['swiss_qrbill'] or ctx['eu_barcodes']
return template.render(ctx, request=request)
return template.render(ctx)
def payment_control_render(self, request: HttpRequest, payment: OrderPayment) -> str:
warning = None

View File

@@ -7,7 +7,6 @@
{% load money %}
{% load unidecode %}
{% load rich_text %}
{% load eventurl %}
{% if pending_description %}
{{ pending_description|rich_text }}
@@ -104,28 +103,3 @@ SCT
{% if swiss_qrbill %}
<link rel="stylesheet" href="{% static "pretixplugins/banktransfer/swisscross.css" %}">
{% endif %}
{% if invoice_email_enabled and has_invoices %}
<form method="post" action="{% eventurl event "plugins:banktransfer:mail_invoice" order=order.code secret=order.secret %}">
{% csrf_token %}
<p>
{% blocktrans trimmed %}
To send the invoice directly to your accounting department, please enter their email address:
{% endblocktrans %}
</p>
<div class="row">
<div class="col-md-9 col-xs-12">
<label for="mail_invoice_email" class="sr-only">{% trans "Invoice recipient email" %}:</label>
<input type="email" name="email" id="mail_invoice_email" class="form-control" value="" required
placeholder="{% trans "Email address" %}" />
</div>
<div class="col-md-3 col-xs-12">
<button class="btn btn-default btn-block">
<span class="fa fa-envelope-o" aria-hidden="true"></span>
{% trans "Send invoice via email" %}
</button>
</div>
</div>
</form>
<hr>
{% endif %}

View File

@@ -19,19 +19,13 @@
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
from django.urls import include, re_path
from django.urls import re_path
from pretix.api.urls import orga_router
from pretix.plugins.banktransfer.api import BankImportJobViewSet
from . import views
event_patterns = [
re_path(r'^banktransfer/', include([
re_path(r'^(?P<order>[^/][^w]+)/(?P<secret>[A-Za-z0-9]+)/mail-invoice/$', views.SendInvoiceMailView.as_view(), name='mail_invoice'),
])),
]
urlpatterns = [
re_path(r'^control/organizer/(?P<organizer>[^/]+)/banktransfer/import/',
views.OrganizerImportView.as_view(),

View File

@@ -44,18 +44,14 @@ from typing import Set
from django import forms
from django.contrib import messages
from django.core.exceptions import ValidationError
from django.core.validators import validate_email
from django.db import transaction
from django.db.models import Count, Q, QuerySet
from django.http import FileResponse, Http404, JsonResponse
from django.http import FileResponse, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.utils.functional import cached_property
from django.utils.timezone import now
from django.utils.translation import gettext as _
from django.views.decorators.clickjacking import xframe_options_exempt
from django.views.generic import DetailView, FormView, ListView, View
from django.views.generic.detail import SingleObjectMixin
from localflavor.generic.forms import BICFormField, IBANFormField
@@ -79,8 +75,6 @@ from pretix.plugins.banktransfer.refund_export import (
build_sepa_xml, get_refund_export_csv,
)
from pretix.plugins.banktransfer.tasks import process_banktransfers
from pretix.presale.views import EventViewMixin
from pretix.presale.views.order import OrderDetailMixin
logger = logging.getLogger('pretix.plugins.banktransfer')
@@ -892,36 +886,3 @@ class OrganizerSepaXMLExportView(OrganizerPermissionRequiredMixin, OrganizerDeta
organizer=self.request.organizer,
pk=self.kwargs.get('id')
)
@method_decorator(xframe_options_exempt, 'dispatch')
class SendInvoiceMailView(EventViewMixin, OrderDetailMixin, View):
def post(self, request, *args, **kwargs):
if not self.order:
raise Http404(_('Unknown order code or not authorized to access this order.'))
try:
validate_email(request.POST['email'])
except ValidationError:
messages.error(request, _('Please enter a valid email address.'))
return redirect(self.get_order_url())
last_payment = self.order.payments.last()
if (not last_payment
or last_payment.provider != BankTransfer.identifier
or last_payment.state != OrderPayment.PAYMENT_STATE_CREATED):
messages.error(request, _('No pending bank transfer payment found. Maybe the order has been paid already?'))
return redirect(self.get_order_url())
if not last_payment.payment_provider.settings.get('invoice_email', as_type=bool):
messages.error(request, _('Sending invoices via email is disabled by the event organizer.'))
return redirect(self.get_order_url())
last_invoice = self.order.invoices.last()
if not last_invoice:
messages.error(request, _('No invoice found, please request an invoice first.'))
return redirect(self.get_order_url())
provider = last_payment.payment_provider
provider.send_invoice_to_alternate_email(self.order, last_invoice, request.POST['email'])
messages.success(request, _('Sending the latest invoice via e-mail to {email}.').format(email=request.POST['email']))
return redirect(self.get_order_url())

View File

@@ -76,11 +76,7 @@ class BaseMailForm(FormPlaceholderMixin, forms.Form):
attachment = CachedFileField(
label=_("Attachment"),
required=False,
ext_whitelist=(
".png", ".jpg", ".gif", ".jpeg", ".pdf", ".txt", ".docx", ".gif", ".svg",
".pptx", ".ppt", ".doc", ".xlsx", ".xls", ".jfif", ".heic", ".heif", ".pages",
".bmp", ".tif", ".tiff"
),
ext_whitelist=settings.FILE_UPLOAD_EXTENSIONS_EMAIL_ATTACHMENT,
help_text=_('Sending an attachment increases the chance of your email not arriving or being sorted into spam folders. We recommend only using PDFs '
'of no more than 2 MB in size.'),
max_size=settings.FILE_UPLOAD_MAX_SIZE_EMAIL_ATTACHMENT

View File

@@ -1,66 +0,0 @@
{% load i18n %}
{% load eventurl %}
{% if ev.location and show_location %}
<div class="info-row">
<span class="fa fa-map-marker fa-fw" aria-hidden="true" title="{% trans "Where does the event happen?" %}"></span>
<p><span class="sr-only">{% trans "Where does the event happen?" %}</span>
{{ ev.location|linebreaksbr }}
</p>
</div>
{% endif %}
{% if ev.settings.show_dates_on_frontpage %}
<div class="info-row">
<span class="fa fa-clock-o fa-fw" aria-hidden="true" title="{% trans "When does the event happen?" %}"></span>
<p><span class="sr-only">{% trans "When does the event happen?" %}</span>
{{ ev.get_date_range_display_as_html }}
{% if event.settings.show_times %}
<br>
<span data-time="{{ ev.date_from.isoformat }}" data-timezone="{{ request.event.timezone }}">
{% with time_human=ev.date_from|date:"TIME_FORMAT" time_24=ev.date_from|time:"H:i" %}
{% blocktrans trimmed with time='<time datetime="'|add:time_24|add:'">'|add:time_human|add:"</time>"|safe %}
Begin: {{ time }}
{% endblocktrans %}
{% endwith %}
</span>
{% if event.settings.show_date_to and ev.date_to %}
<br>
<span data-time="{{ ev.date_to.isoformat }}" data-timezone="{{ request.event.timezone }}">
{% with time_human=ev.date_to|date:"TIME_FORMAT" time_24=ev.date_to|time:"H:i" %}
{% blocktrans trimmed with time='<time datetime="'|add:time_24|add:'">'|add:time_human|add:"</time>"|safe %}
End: {{ time }}
{% endblocktrans %}
{% endwith %}
</span>
{% endif %}
{% endif %}
{% if ev.date_admission %}
<br>
{% if ev.date_admission|date:"SHORT_DATE_FORMAT" == ev.date_from|date:"SHORT_DATE_FORMAT" %}
<span data-time="{{ ev.date_admission.isoformat }}" data-timezone="{{ request.event.timezone }}">
{% with time_human=ev.date_admission|date:"TIME_FORMAT" time_24=ev.date_admission|time:"H:i" %}
{% blocktrans trimmed with time='<time datetime="'|add:time_24|add:'">'|add:time_human|add:"</time>"|safe %}
Admission: {{ time }}
{% endblocktrans %}
{% endwith %}
</span>
{% else %}
<span data-time="{{ ev.date_admission.isoformat }}" data-timezone="{{ request.event.timezone }}">
{% with datetime_human=ev.date_admission|date:"SHORT_DATETIME_FORMAT" datetime_iso=ev.date_admission|time:"Y-m-d H:i" %}
{% blocktrans trimmed with datetime='<time datetime="'|add:datetime_iso|add:'">'|add:datetime_human|add:"</time>"|safe %}
Admission: {{ datetime }}
{% endblocktrans %}
{% endwith %}
</span>
{% endif %}
{% endif %}
<br>
{% if subevent %}
<a href="{% eventurl event "presale:event.ical.download" subevent=subevent.pk %}">
{% else %}
<a href="{% eventurl event "presale:event.ical.download" %}">
{% endif %}
{% trans "Add to Calendar" %}
</a>
</p>
</div>
{% endif %}

View File

@@ -162,8 +162,73 @@
{% endif %}
{% if not cart_namespace or subevent %}
<div>
{% include "pretixpresale/event/fragment_event_info.html" with event=request.event subevent=subevent ev=ev show_location=True %}
{% if ev.location %}
<div class="info-row">
<span class="fa fa-map-marker fa-fw" aria-hidden="true" title="{% trans "Where does the event happen?" %}"></span>
<p><span class="sr-only">{% trans "Where does the event happen?" %}</span>
{{ ev.location|linebreaksbr }}
</p>
</div>
{% endif %}
{% if ev.settings.show_dates_on_frontpage %}
<div class="info-row">
<span class="fa fa-clock-o fa-fw" aria-hidden="true" title="{% trans "When does the event happen?" %}"></span>
<p><span class="sr-only">{% trans "When does the event happen?" %}</span>
{{ ev.get_date_range_display_as_html }}
{% if event.settings.show_times %}
<br>
<span data-time="{{ ev.date_from.isoformat }}" data-timezone="{{ request.event.timezone }}">
{% with time_human=ev.date_from|date:"TIME_FORMAT" time_24=ev.date_from|time:"H:i" %}
{% blocktrans trimmed with time='<time datetime="'|add:time_24|add:'">'|add:time_human|add:"</time>"|safe %}
Begin: {{ time }}
{% endblocktrans %}
{% endwith %}
</span>
{% if event.settings.show_date_to and ev.date_to %}
<br>
<span data-time="{{ ev.date_to.isoformat }}" data-timezone="{{ request.event.timezone }}">
{% with time_human=ev.date_to|date:"TIME_FORMAT" time_24=ev.date_to|time:"H:i" %}
{% blocktrans trimmed with time='<time datetime="'|add:time_24|add:'">'|add:time_human|add:"</time>"|safe %}
End: {{ time }}
{% endblocktrans %}
{% endwith %}
</span>
{% endif %}
{% endif %}
{% if ev.date_admission %}
<br>
{% if ev.date_admission|date:"SHORT_DATE_FORMAT" == ev.date_from|date:"SHORT_DATE_FORMAT" %}
<span data-time="{{ ev.date_admission.isoformat }}" data-timezone="{{ request.event.timezone }}">
{% with time_human=ev.date_admission|date:"TIME_FORMAT" time_24=ev.date_admission|time:"H:i" %}
{% blocktrans trimmed with time='<time datetime="'|add:time_24|add:'">'|add:time_human|add:"</time>"|safe %}
Admission: {{ time }}
{% endblocktrans %}
{% endwith %}
</span>
{% else %}
<span data-time="{{ ev.date_admission.isoformat }}" data-timezone="{{ request.event.timezone }}">
{% with datetime_human=ev.date_admission|date:"SHORT_DATETIME_FORMAT" datetime_iso=ev.date_admission|time:"Y-m-d H:i" %}
{% blocktrans trimmed with datetime='<time datetime="'|add:datetime_iso|add:'">'|add:datetime_human|add:"</time>"|safe %}
Admission: {{ datetime }}
{% endblocktrans %}
{% endwith %}
</span>
{% endif %}
{% endif %}
<br>
{% if subevent %}
<a href="{% eventurl event "presale:event.ical.download" subevent=subevent.pk %}">
{% else %}
<a href="{% eventurl event "presale:event.ical.download" %}">
{% endif %}
{% trans "Add to Calendar" %}
</a>
</p>
</div>
{% endif %}
</div>
{% eventsignal event "pretix.presale.signals.front_page_top" request=request subevent=subevent %}
{% endif %}

View File

@@ -13,28 +13,63 @@
{% include "pretixpresale/event/fragment_cart_box.html" with open=request.GET.show_cart %}
{% endif %}
<h2>{% trans "Voucher redemption" %}</h2>
{% if subevent %}
<h2>{% trans "Voucher redemption" %}</h2>
{% if request.GET.subevent and subevent.pk|stringformat:"i" != request.GET.subevent %}
<div class="alert alert-warning">
{% trans "This voucher is valid only for the following specific date and time." %}
</div>
{% endif %}
<h3>{{ subevent.name }}</h3>
{% include "pretixpresale/event/fragment_event_info.html" with event=request.event subevent=subevent ev=subevent show_location=True %}
{% else %}
{% if event_logo and event_logo_show_title %}
<h2 class="content-header">
{{ event.name }}
{% if request.event.settings.show_dates_on_frontpage %}
<small>{{ event.get_date_range_display_as_html }}</small>
{% endif %}
</h2>
{% include "pretixpresale/event/fragment_event_info.html" with event=request.event subevent=None ev=request.event show_location=True %}
<h3>{% trans "Voucher redemption" %}</h3>
{% else %}
<h2>{% trans "Voucher redemption" %}</h2>
{% endif %}
{% with ev=subevent %}
<div class="info-row">
<span class="fa fa-clock-o fa-fw" aria-hidden="true"></span>
<p>
{{ ev.get_date_range_display_as_html }}
{% if event.settings.show_times %}
<br>
{% with time_human=ev.date_from|date:"TIME_FORMAT" time_24=ev.date_from|time:"H:i" %}
{% blocktrans trimmed with time='<time datetime="'|add:time_24|add:'">'|add:time_human|add:"</time>"|safe %}
Begin: {{ time }}
{% endblocktrans %}
{% endwith %}
{% if event.settings.show_date_to and ev.date_to %}
<br>
{% with time_human=ev.date_to|date:"TIME_FORMAT" time_24=ev.date_to|time:"H:i" %}
{% blocktrans trimmed with time='<time datetime="'|add:time_24|add:'">'|add:time_human|add:"</time>"|safe %}
End: {{ time }}
{% endblocktrans %}
{% endwith %}
{% endif %}
{% endif %}
{% if ev.date_admission %}
<br>
{% if ev.date_admission|date:"SHORT_DATE_FORMAT" == ev.date_from|date:"SHORT_DATE_FORMAT" %}
{% with time_human=ev.date_admission|date:"TIME_FORMAT" time_24=ev.date_admission|time:"H:i" %}
{% blocktrans trimmed with time='<time datetime="'|add:time_24|add:'">'|add:time_human|add:"</time>"|safe %}
Admission: {{ time }}
{% endblocktrans %}
{% endwith %}
{% else %}
{% with datetime_human=ev.date_admission|date:"SHORT_DATETIME_FORMAT" datetime_iso=ev.date_admission|time:"Y-m-d H:i" %}
{% blocktrans trimmed with datetime='<time datetime="'|add:datetime_iso|add:'">'|add:datetime_human|add:"</time>"|safe %}
Admission: {{ datetime }}
{% endblocktrans %}
{% endwith %}
{% endif %}
{% endif %}
<br>
{% if subevent %}
<a href="{% eventurl event "presale:event.ical.download" subevent=subevent.pk %}">
{% else %}
<a href="{% eventurl event "presale:event.ical.download" %}">
{% endif %}
{% trans "Add to Calendar" %}
</a>
</p>
</div>
{% endwith %}
{% endif %}
<p>

View File

@@ -188,13 +188,13 @@ if SITE_URL.endswith('/'):
CSRF_TRUSTED_ORIGINS = [urlparse(SITE_URL).scheme + '://' + urlparse(SITE_URL).hostname]
TRUST_X_FORWARDED_FOR = config.get('pretix', 'trust_x_forwarded_for', fallback=False)
USE_X_FORWARDED_HOST = config.get('pretix', 'trust_x_forwarded_host', fallback=False)
TRUST_X_FORWARDED_FOR = config.getboolean('pretix', 'trust_x_forwarded_for', fallback=False)
USE_X_FORWARDED_HOST = config.getboolean('pretix', 'trust_x_forwarded_host', fallback=False)
REQUEST_ID_HEADER = config.get('pretix', 'request_id_header', fallback=False)
if config.get('pretix', 'trust_x_forwarded_proto', fallback=False):
if config.getboolean('pretix', 'trust_x_forwarded_proto', fallback=False):
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
PRETIX_PLUGINS_DEFAULT = config.get('pretix', 'plugins_default',
@@ -733,4 +733,5 @@ FILE_UPLOAD_MAX_SIZE_EMAIL_ATTACHMENT = 1024 * 1024 * config.getint("pretix_file
FILE_UPLOAD_MAX_SIZE_EMAIL_AUTO_ATTACHMENT = 1024 * 1024 * config.getint("pretix_file_upload", "max_size_email_auto_attachment", fallback=1)
FILE_UPLOAD_MAX_SIZE_OTHER = 1024 * 1024 * config.getint("pretix_file_upload", "max_size_other", fallback=10)
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' # sadly. we would prefer BigInt, and should use it for all new models but the migration will be hard

View File

@@ -80,15 +80,7 @@
"DATE_INPUT_FORMATS": [
"%Y-%m-%d",
"%m/%d/%Y",
"%m/%d/%y",
"%b %d %Y",
"%b %d, %Y",
"%d %b %Y",
"%d %b, %Y",
"%B %d %Y",
"%B %d, %Y",
"%d %B %Y",
"%d %B, %Y"
"%m/%d/%y"
],
"DECIMAL_SEPARATOR": ".",
"FIRST_DAY_OF_WEEK": 0,

View File

@@ -8,7 +8,7 @@
"name": "pretix",
"version": "0.0.0",
"dependencies": {
"@babel/core": "^7.22.9",
"@babel/core": "^7.22.5",
"@babel/preset-env": "^7.22.9",
"@rollup/plugin-babel": "^6.0.3",
"@rollup/plugin-node-resolve": "^15.1.0",
@@ -50,25 +50,25 @@
}
},
"node_modules/@babel/core": {
"version": "7.22.9",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.9.tgz",
"integrity": "sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.5.tgz",
"integrity": "sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg==",
"dependencies": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.22.5",
"@babel/generator": "^7.22.9",
"@babel/helper-compilation-targets": "^7.22.9",
"@babel/helper-module-transforms": "^7.22.9",
"@babel/helpers": "^7.22.6",
"@babel/parser": "^7.22.7",
"@babel/generator": "^7.22.5",
"@babel/helper-compilation-targets": "^7.22.5",
"@babel/helper-module-transforms": "^7.22.5",
"@babel/helpers": "^7.22.5",
"@babel/parser": "^7.22.5",
"@babel/template": "^7.22.5",
"@babel/traverse": "^7.22.8",
"@babel/traverse": "^7.22.5",
"@babel/types": "^7.22.5",
"convert-source-map": "^1.7.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
"json5": "^2.2.2",
"semver": "^6.3.1"
"semver": "^6.3.0"
},
"engines": {
"node": ">=6.9.0"
@@ -98,9 +98,9 @@
}
},
"node_modules/@babel/generator": {
"version": "7.22.9",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.9.tgz",
"integrity": "sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.5.tgz",
"integrity": "sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==",
"dependencies": {
"@babel/types": "^7.22.5",
"@jridgewell/gen-mapping": "^0.3.2",
@@ -308,21 +308,21 @@
}
},
"node_modules/@babel/helper-module-transforms": {
"version": "7.22.9",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz",
"integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz",
"integrity": "sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw==",
"dependencies": {
"@babel/helper-environment-visitor": "^7.22.5",
"@babel/helper-module-imports": "^7.22.5",
"@babel/helper-simple-access": "^7.22.5",
"@babel/helper-split-export-declaration": "^7.22.6",
"@babel/helper-validator-identifier": "^7.22.5"
"@babel/helper-split-export-declaration": "^7.22.5",
"@babel/helper-validator-identifier": "^7.22.5",
"@babel/template": "^7.22.5",
"@babel/traverse": "^7.22.5",
"@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0"
}
},
"node_modules/@babel/helper-optimise-call-expression": {
@@ -447,12 +447,12 @@
}
},
"node_modules/@babel/helpers": {
"version": "7.22.6",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.6.tgz",
"integrity": "sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.5.tgz",
"integrity": "sha512-pSXRmfE1vzcUIDFQcSGA5Mr+GxBV9oiRKDuDxXvWQQBCh8HoIjs/2DlDB7H8smac1IVrB9/xdXj2N3Wol9Cr+Q==",
"dependencies": {
"@babel/template": "^7.22.5",
"@babel/traverse": "^7.22.6",
"@babel/traverse": "^7.22.5",
"@babel/types": "^7.22.5"
},
"engines": {
@@ -473,9 +473,9 @@
}
},
"node_modules/@babel/parser": {
"version": "7.22.7",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz",
"integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.5.tgz",
"integrity": "sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==",
"bin": {
"parser": "bin/babel-parser.js"
},
@@ -1631,17 +1631,17 @@
}
},
"node_modules/@babel/traverse": {
"version": "7.22.8",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.8.tgz",
"integrity": "sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.5.tgz",
"integrity": "sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==",
"dependencies": {
"@babel/code-frame": "^7.22.5",
"@babel/generator": "^7.22.7",
"@babel/generator": "^7.22.5",
"@babel/helper-environment-visitor": "^7.22.5",
"@babel/helper-function-name": "^7.22.5",
"@babel/helper-hoist-variables": "^7.22.5",
"@babel/helper-split-export-declaration": "^7.22.6",
"@babel/parser": "^7.22.7",
"@babel/helper-split-export-declaration": "^7.22.5",
"@babel/parser": "^7.22.5",
"@babel/types": "^7.22.5",
"debug": "^4.1.0",
"globals": "^11.1.0"
@@ -4160,25 +4160,25 @@
"integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ=="
},
"@babel/core": {
"version": "7.22.9",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.9.tgz",
"integrity": "sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.5.tgz",
"integrity": "sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg==",
"requires": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.22.5",
"@babel/generator": "^7.22.9",
"@babel/helper-compilation-targets": "^7.22.9",
"@babel/helper-module-transforms": "^7.22.9",
"@babel/helpers": "^7.22.6",
"@babel/parser": "^7.22.7",
"@babel/generator": "^7.22.5",
"@babel/helper-compilation-targets": "^7.22.5",
"@babel/helper-module-transforms": "^7.22.5",
"@babel/helpers": "^7.22.5",
"@babel/parser": "^7.22.5",
"@babel/template": "^7.22.5",
"@babel/traverse": "^7.22.8",
"@babel/traverse": "^7.22.5",
"@babel/types": "^7.22.5",
"convert-source-map": "^1.7.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
"json5": "^2.2.2",
"semver": "^6.3.1"
"semver": "^6.3.0"
},
"dependencies": {
"json5": {
@@ -4194,9 +4194,9 @@
}
},
"@babel/generator": {
"version": "7.22.9",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.9.tgz",
"integrity": "sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.5.tgz",
"integrity": "sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==",
"requires": {
"@babel/types": "^7.22.5",
"@jridgewell/gen-mapping": "^0.3.2",
@@ -4355,15 +4355,18 @@
}
},
"@babel/helper-module-transforms": {
"version": "7.22.9",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz",
"integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz",
"integrity": "sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw==",
"requires": {
"@babel/helper-environment-visitor": "^7.22.5",
"@babel/helper-module-imports": "^7.22.5",
"@babel/helper-simple-access": "^7.22.5",
"@babel/helper-split-export-declaration": "^7.22.6",
"@babel/helper-validator-identifier": "^7.22.5"
"@babel/helper-split-export-declaration": "^7.22.5",
"@babel/helper-validator-identifier": "^7.22.5",
"@babel/template": "^7.22.5",
"@babel/traverse": "^7.22.5",
"@babel/types": "^7.22.5"
}
},
"@babel/helper-optimise-call-expression": {
@@ -4449,12 +4452,12 @@
}
},
"@babel/helpers": {
"version": "7.22.6",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.6.tgz",
"integrity": "sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.5.tgz",
"integrity": "sha512-pSXRmfE1vzcUIDFQcSGA5Mr+GxBV9oiRKDuDxXvWQQBCh8HoIjs/2DlDB7H8smac1IVrB9/xdXj2N3Wol9Cr+Q==",
"requires": {
"@babel/template": "^7.22.5",
"@babel/traverse": "^7.22.6",
"@babel/traverse": "^7.22.5",
"@babel/types": "^7.22.5"
}
},
@@ -4469,9 +4472,9 @@
}
},
"@babel/parser": {
"version": "7.22.7",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz",
"integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q=="
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.5.tgz",
"integrity": "sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q=="
},
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
"version": "7.22.5",
@@ -5224,17 +5227,17 @@
}
},
"@babel/traverse": {
"version": "7.22.8",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.8.tgz",
"integrity": "sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.5.tgz",
"integrity": "sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==",
"requires": {
"@babel/code-frame": "^7.22.5",
"@babel/generator": "^7.22.7",
"@babel/generator": "^7.22.5",
"@babel/helper-environment-visitor": "^7.22.5",
"@babel/helper-function-name": "^7.22.5",
"@babel/helper-hoist-variables": "^7.22.5",
"@babel/helper-split-export-declaration": "^7.22.6",
"@babel/parser": "^7.22.7",
"@babel/helper-split-export-declaration": "^7.22.5",
"@babel/parser": "^7.22.5",
"@babel/types": "^7.22.5",
"debug": "^4.1.0",
"globals": "^11.1.0"

View File

@@ -4,7 +4,7 @@
"private": true,
"scripts": {},
"dependencies": {
"@babel/core": "^7.22.9",
"@babel/core": "^7.22.5",
"@babel/preset-env": "^7.22.9",
"@rollup/plugin-babel": "^6.0.3",
"@rollup/plugin-node-resolve": "^15.1.0",

View File

@@ -4,20 +4,9 @@ setup_collapsible_details = function (el) {
el.find('details.sneak-peek:not([open])').each(function() {
this.open = true;
var $elements = $("> :not(summary)", this).show().filter(':not(.sneak-peek-trigger)');
var $elements = $("> :not(summary)", this).show().filter(':not(.sneak-peek-trigger)').attr('aria-hidden', 'true');
var container = this;
if (Array.prototype.reduce.call($elements, function (h, e) {
return h + $(e).outerHeight();
}, 0) < 200) {
$(".sneak-peek-trigger", this).remove();
$(container).removeClass('sneak-peek');
container.style.removeProperty('height');
return;
}
$elements.attr('aria-hidden', 'true');
var trigger = $('summary, .sneak-peek-trigger button', container);
function onclick(e) {
e.preventDefault();

View File

@@ -1,7 +1,7 @@
/*global $,u2f */
$(function () {
$('.sidebar .dropdown, ul.navbar-nav .dropdown, .navbar-events-collapse').on('shown.bs.collapse shown.bs.dropdown', function () {
$(this).parent().find("input").val("").trigger('forceRunQuery').focus();
$(this).parent().find("input").val("").change().focus();
});
$('.dropdown-menu .form-box input').click(function (e) {
e.stopPropagation();
@@ -11,22 +11,11 @@ $(function () {
var $container = $(this);
var $query = $(this).find('[data-typeahead-query]').length ? $(this).find('[data-typeahead-query]') : $($(this).attr("data-typeahead-field"));
$container.find("li:not(.query-holder)").remove();
var lastQuery = null;
var lastQuery = "";
var runQueryTimeout = null;
var loadIndicatorTimeout = null;
function showLoadIndicator() {
$container.find("li:not(.query-holder)").remove();
$container.append("<li class='loading'><span class='fa fa-4x fa-cog fa-spin'></span></li>");
$container.toggleClass('focused', $query.is(":focus") && $container.children().length > 0);
}
function runQuery() {
var thisQuery = $query.val();
if (thisQuery === lastQuery) return;
lastQuery = $query.val();
window.clearTimeout(loadIndicatorTimeout)
loadIndicatorTimeout = window.setTimeout(showLoadIndicator, 80)
var thisQuery = $query.val();
$.getJSON(
$container.attr("data-source") + "?query=" + encodeURIComponent($query.val()) + (typeof $container.attr("data-organizer") !== "undefined" ? "&organizer=" + $container.attr("data-organizer") : ""),
function (data) {
@@ -34,74 +23,107 @@ $(function () {
// Lost race condition
return;
}
window.clearTimeout(loadIndicatorTimeout);
$container.find("li:not(.query-holder)").remove();
$.each(data.results, function (i, res) {
let $linkContent = $("<div>");
if (res.type === "organizer") {
$linkContent.append(
$("<span>").addClass("event-name-full").append(
$("<span>").addClass("fa fa-users fa-fw")
).append(" ").append($("<div>").text(res.name).html())
)
$container.append(
$("<li>").append(
$("<a>").attr("href", res.url).append(
$("<div>").append(
$("<span>").addClass("event-name-full").append(
$("<span>").addClass("fa fa-users fa-fw")
).append(" ").append($("<div>").text(res.name).html())
)
).on("mousedown", function (event) {
if ($(this).length) {
location.href = $(this).attr("href");
}
$(this).parent().addClass("active");
event.preventDefault();
event.stopPropagation();
})
)
);
} else if (res.type === "order" || res.type === "voucher") {
$linkContent.append(
$("<span>").addClass("event-name-full").append($("<div>").text(res.title).html())
).append(
$("<span>").addClass("event-organizer").append(
$("<span>").addClass("fa fa-calendar fa-fw")
).append(" ").append($("<div>").text(res.event).html())
)
$container.append(
$("<li>").append(
$("<a>").attr("href", res.url).append(
$("<div>").append(
$("<span>").addClass("event-name-full").append($("<div>").text(res.title).html())
).append(
$("<span>").addClass("event-organizer").append(
$("<span>").addClass("fa fa-calendar fa-fw")
).append(" ").append($("<div>").text(res.event).html())
)
).on("mousedown", function (event) {
if ($(this).length) {
location.href = $(this).attr("href");
}
$(this).parent().addClass("active");
event.preventDefault();
event.stopPropagation();
})
)
);
} else if (res.type === "user") {
$linkContent.append(
$("<span>").addClass("event-name-full").append(
$("<span>").addClass("fa fa-user fa-fw")
).append(" ").append($("<div>").text(res.name).html())
)
$container.append(
$("<li>").append(
$("<a>").attr("href", res.url).append(
$("<div>").append(
$("<span>").addClass("event-name-full").append(
$("<span>").addClass("fa fa-user fa-fw")
).append(" ").append($("<div>").text(res.name).html())
)
).on("mousedown", function (event) {
if ($(this).length) {
location.href = $(this).attr("href");
}
$(this).parent().addClass("active");
event.preventDefault();
event.stopPropagation();
})
)
);
} else {
$linkContent.append(
$("<span>").addClass("event-name-full").append($("<div>").text(res.name).html())
).append(
$("<span>").addClass("event-organizer").append(
$("<span>").addClass("fa fa-users fa-fw")
).append(" ").append($("<div>").text(res.organizer).html())
).append(
$("<span>").addClass("event-daterange").append(
$("<span>").addClass("fa fa-calendar fa-fw")
).append(" ").append(res.date_range)
)
$container.append(
$("<li>").append(
$("<a>").attr("href", res.url).append(
$("<div>").append(
$("<span>").addClass("event-name-full").append($("<div>").text(res.name).html())
).append(
$("<span>").addClass("event-organizer").append(
$("<span>").addClass("fa fa-users fa-fw")
).append(" ").append($("<div>").text(res.organizer).html())
).append(
$("<span>").addClass("event-daterange").append(
$("<span>").addClass("fa fa-calendar fa-fw")
).append(" ").append(res.date_range)
)
).on("mousedown", function (event) {
if ($(this).length) {
location.href = $(this).attr("href");
}
$(this).parent().addClass("active");
event.preventDefault();
event.stopPropagation();
})
)
);
}
$container.append(
$("<li>").append(
$("<a>").attr("href", res.url).append(
$linkContent
).on("mousedown", function (event) {
if ($(this).length) {
location.href = $(this).attr("href");
}
$(this).parent().addClass("active");
event.preventDefault();
event.stopPropagation();
})
)
);
});
$container.toggleClass('focused', $query.is(":focus") && $container.children().length > 0);
}
);
}
$query.on("forceRunQuery", function () {
runQuery();
});
$query.on("input", function () {
$query.on("change", function () {
if ($container.attr("data-typeahead-field") && $query.val() === "") {
$container.removeClass('focused');
$container.find("li:not(.query-holder)").remove();
lastQuery = null;
return;
}
window.clearTimeout(runQueryTimeout)
if (runQueryTimeout != null) {
window.clearTimeout(runQueryTimeout)
}
runQueryTimeout = window.setTimeout(runQuery, 250)
});
$query.on("keydown", function (event) {
@@ -155,6 +177,8 @@ $(function () {
event.preventDefault();
event.stopPropagation();
return true;
} else {
$(this).change();
}
});
});

View File

@@ -243,7 +243,7 @@ div.mail-preview {
}
}
.input-group-btn .btn {
padding-bottom: 8px;
padding-bottom: 7px;
}
.reldatetime {
input[type=text], select {

View File

@@ -412,11 +412,6 @@ body.loading #wrapper {
white-space: nowrap;
overflow: hidden;
}
.loading {
padding: 10px;
text-align: center;
color: lightgrey;
}
}
.sidebar-content {

View File

@@ -54,10 +54,10 @@ $(function () {
wallets.forEach(function(wallet) {
const labels = $('[data-wallets*='+wallet+'] + label strong, [data-wallets*='+wallet+'] + strong')
.append('<span class="wallet wallet-loading" data-wallet="'+wallet+'"> <i aria-hidden="true" class="fa fa-cog fa-spin"></i></span>')
.append('<span class="wallet wallet-loading"> <i aria-hidden="true" class="fa fa-cog fa-spin"></i></span>')
walletdetection[wallet]()
.then(function(result) {
const spans = labels.find(".wallet-loading[data-wallet=" + wallet + "]");
const spans = labels.find(".wallet-loading:nth-of-type(1)");
if (result) {
spans.removeClass('wallet-loading').hide().text(', ' + walletdetection.name_map[wallet]).fadeIn(300);
} else {
@@ -65,7 +65,7 @@ $(function () {
}
})
.catch(function(result) {
labels.find(".wallet-loading[data-wallet=" + wallet + "]").remove();
labels.find(".wallet-loading:nth-of-type(1)").remove();
})
});
});

View File

@@ -42,7 +42,7 @@ EMAIL_BACKEND = EMAIL_CUSTOM_SMTP_BACKEND = 'django.core.mail.backends.locmem.Em
COMPRESS_ENABLED = COMPRESS_OFFLINE = False
COMPRESS_CACHE_BACKEND = 'testcache'
STORAGES["staticfiles"]["BACKEND"] = 'django.contrib.staticfiles.storage.StaticFilesStorage'
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.StaticFilesStorage'
PRETIX_INSTANCE_NAME = 'pretix.eu'
COMPRESS_PRECOMPILERS_ORIGINAL = COMPRESS_PRECOMPILERS

View File

@@ -21,8 +21,6 @@ addopts = --reruns 3 -rw
filterwarnings =
error
ignore:The 'warn' method is deprecated:DeprecationWarning
ignore::django.utils.deprecation.RemovedInDjango51Warning:django.core.files.storage
ignore:.*index_together.*:django.utils.deprecation.RemovedInDjango51Warning:
ignore::DeprecationWarning:mt940
ignore::DeprecationWarning:cbor2
ignore::DeprecationWarning:markdown

View File

@@ -1794,8 +1794,6 @@ def test_pdf_data(token_client, organizer, event, order, django_assert_max_num_q
))
assert resp.status_code == 200
assert resp.data['positions'][0].get('pdf_data')
assert resp.data['positions'][0]['pdf_data']['positionid'] == '1'
assert resp.data['positions'][0]['pdf_data']['order'] == order.code
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/{}/'.format(
organizer.slug, event.slug, order.code
))
@@ -1809,8 +1807,6 @@ def test_pdf_data(token_client, organizer, event, order, django_assert_max_num_q
))
assert resp.status_code == 200
assert resp.data['results'][0]['positions'][0].get('pdf_data')
assert resp.data['results'][0]['positions'][0]['pdf_data']['positionid'] == '1'
assert resp.data['results'][0]['positions'][0]['pdf_data']['order'] == order.code
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/'.format(
organizer.slug, event.slug
))
@@ -1824,8 +1820,6 @@ def test_pdf_data(token_client, organizer, event, order, django_assert_max_num_q
))
assert resp.status_code == 200
assert resp.data['results'][0].get('pdf_data')
assert resp.data['results'][0]['pdf_data']['positionid'] == '1'
assert resp.data['results'][0]['pdf_data']['order'] == order.code
resp = token_client.get('/api/v1/organizers/{}/events/{}/orderpositions/'.format(
organizer.slug, event.slug
))
@@ -1840,8 +1834,6 @@ def test_pdf_data(token_client, organizer, event, order, django_assert_max_num_q
))
assert resp.status_code == 200
assert resp.data.get('pdf_data')
assert resp.data['pdf_data']['positionid'] == '1'
assert resp.data['pdf_data']['order'] == order.code
resp = token_client.get('/api/v1/organizers/{}/events/{}/orderpositions/{}/'.format(
organizer.slug, event.slug, posid
))

View File

@@ -940,8 +940,8 @@ def test_rules_reasoning_prefer_number_over_date(event, position, clist):
@pytest.mark.django_db(transaction=True)
def test_position_queries(django_assert_max_num_queries, position, clist):
with django_assert_max_num_queries(13) as captured:
def test_position_queries(django_assert_num_queries, position, clist):
with django_assert_num_queries(12 if 'sqlite' in settings.DATABASES['default']['ENGINE'] else 11) as captured:
perform_checkin(position, clist, {})
if 'sqlite' not in settings.DATABASES['default']['ENGINE']:
assert any('FOR UPDATE' in s['sql'] for s in captured)

View File

@@ -38,10 +38,10 @@ class I18nStringTest(TestCase):
'en': 'Hello'
}
s = LazyI18nString(data)
with translation.override('en'):
self.assertEqual(str(s), 'Hello')
with translation.override('de'):
self.assertEqual(str(s), 'Hallo')
translation.activate('en')
self.assertEqual(str(s), 'Hello')
translation.activate('de')
self.assertEqual(str(s), 'Hallo')
def test_similar_translations(self):
data = {
@@ -50,57 +50,57 @@ class I18nStringTest(TestCase):
'de-informal': 'Du'
}
s = LazyI18nString(data)
with translation.override('de'):
self.assertEqual(str(s), 'Sie')
with translation.override('de-informal'):
self.assertEqual(str(s), 'Du')
translation.activate('de')
self.assertEqual(str(s), 'Sie')
translation.activate('de-informal')
self.assertEqual(str(s), 'Du')
data = {
'en': 'You',
'de-informal': 'Du'
}
s = LazyI18nString(data)
with translation.override('de'):
self.assertEqual(str(s), 'Du')
with translation.override('de-informal'):
self.assertEqual(str(s), 'Du')
translation.activate('de')
self.assertEqual(str(s), 'Du')
translation.activate('de-informal')
self.assertEqual(str(s), 'Du')
data = {
'en': 'You',
'de': 'Sie'
}
s = LazyI18nString(data)
with translation.override('de'):
self.assertEqual(str(s), 'Sie')
with translation.override('de-informal'):
self.assertEqual(str(s), 'Sie')
translation.activate('de')
self.assertEqual(str(s), 'Sie')
translation.activate('de-informal')
self.assertEqual(str(s), 'Sie')
def test_missing_default_translation(self):
data = {
'de': 'Hallo',
}
s = LazyI18nString(data)
with translation.override('en'):
self.assertEqual(str(s), 'Hallo')
with translation.override('de'):
self.assertEqual(str(s), 'Hallo')
translation.activate('en')
self.assertEqual(str(s), 'Hallo')
translation.activate('de')
self.assertEqual(str(s), 'Hallo')
def test_missing_translation(self):
data = {
'en': 'Hello',
}
s = LazyI18nString(data)
with translation.override('en'):
self.assertEqual(str(s), 'Hello')
with translation.override('de'):
self.assertEqual(str(s), 'Hello')
translation.activate('en')
self.assertEqual(str(s), 'Hello')
translation.activate('de')
self.assertEqual(str(s), 'Hello')
def test_legacy_string(self):
s = LazyI18nString("Hello")
with translation.override('en'):
self.assertEqual(str(s), 'Hello')
with translation.override('de'):
self.assertEqual(str(s), 'Hello')
translation.activate('en')
self.assertEqual(str(s), 'Hello')
translation.activate('de')
self.assertEqual(str(s), 'Hello')
def test_none(self):
s = LazyI18nString(None)
@@ -124,10 +124,10 @@ class I18nFieldTest(TestCase):
obj = ItemCategory.objects.create(event=self.event, name="Hello")
obj = ItemCategory.objects.get(id=obj.id)
self.assertIsInstance(obj.name, LazyI18nString)
with translation.override('en'):
self.assertEqual(str(obj.name), "Hello")
with translation.override('de'):
self.assertEqual(str(obj.name), "Hello")
translation.activate('en')
self.assertEqual(str(obj.name), "Hello")
translation.activate('de')
self.assertEqual(str(obj.name), "Hello")
def test_save_load_cycle_i18n_string(self):
obj = ItemCategory.objects.create(event=self.event,
@@ -139,7 +139,7 @@ class I18nFieldTest(TestCase):
))
obj = ItemCategory.objects.get(id=obj.id)
self.assertIsInstance(obj.name, LazyI18nString)
with translation.override('en'):
self.assertEqual(str(obj.name), "Hello")
with translation.override('de'):
self.assertEqual(str(obj.name), "Hallo")
translation.activate('en')
self.assertEqual(str(obj.name), "Hello")
translation.activate('de')
self.assertEqual(str(obj.name), "Hallo")

View File

@@ -71,7 +71,6 @@ def inputfile_factory():
'I': 'Foo',
'J': '2021-06-28 11:00:00',
'K': '06221/32177-50',
'L': 'True'
},
{
'A': 'Daniel',
@@ -85,7 +84,6 @@ def inputfile_factory():
'I': 'Bar',
'J': '2021-06-28 11:00:00',
'K': '+4962213217750',
'L': 'False',
},
{},
{
@@ -100,11 +98,10 @@ def inputfile_factory():
'I': 'Foo,Bar',
'J': '2021-06-28 11:00:00',
'K': '',
'L': '',
},
]
f = StringIO()
w = csv.DictWriter(f, ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L'], dialect=csv.excel)
w = csv.DictWriter(f, ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K'], dialect=csv.excel)
w.writeheader()
w.writerows(d)
f.seek(0)
@@ -798,37 +795,22 @@ def test_import_question_validate(user, event, item):
@scopes_disabled()
def test_import_question_valid(user, event, item):
settings = dict(DEFAULT_SETTINGS)
q1 = event.questions.create(question='Foo', type=Question.TYPE_CHOICE_MULTIPLE)
o1 = q1.options.create(answer='Foo', identifier='Foo')
o2 = q1.options.create(answer='Bar', identifier='Bar')
q2 = event.questions.create(question='Boolean', type=Question.TYPE_BOOLEAN)
q = event.questions.create(question='Foo', type=Question.TYPE_CHOICE_MULTIPLE)
o1 = q.options.create(answer='Foo', identifier='Foo')
o2 = q.options.create(answer='Bar', identifier='Bar')
settings['item'] = 'static:{}'.format(item.pk)
settings['attendee_email'] = 'csv:C'
settings['question_{}'.format(q1.pk)] = 'csv:I'
settings['question_{}'.format(q2.pk)] = 'csv:L'
settings['question_{}'.format(q.pk)] = 'csv:I'
import_orders.apply(
args=(event.pk, inputfile_factory().id, settings, 'en', user.pk)
).get()
assert QuestionAnswer.objects.filter(question=q1).count() == 3
assert QuestionAnswer.objects.filter(question=q).count() == 3
a1 = OrderPosition.objects.get(attendee_email='schneider@example.org').answers.first()
assert a1.question == q1
assert a1.question == q
assert list(a1.options.all()) == [o1]
a3 = OrderPosition.objects.get(attendee_email__isnull=True).answers.first()
assert a3.question == q1
assert a3.question == q
assert set(a3.options.all()) == {o1, o2}
# Boolean question: One True, One False, one empty == 2 Question answers
assert QuestionAnswer.objects.filter(question=q2).count() == 2
a4 = OrderPosition.objects.get(attendee_email='schneider@example.org').answers.last()
assert a4.question == q2
assert a4.answer == 'True'
a5 = OrderPosition.objects.get(attendee_email='daniel@example.org').answers.last()
assert a5.question == q2
assert a5.answer == 'False'
a6 = OrderPosition.objects.get(attendee_email__isnull=True).answers
assert a6.count() == 1
assert a6.first().question == q1
assert a6.last().question == q1
# TODO: validate question

View File

@@ -2228,7 +2228,6 @@ class OrderChangeManagerTests(TestCase):
@classscope(attr='o')
def test_split_and_change_higher(self):
self.order.status = Order.STATUS_PAID
self.order.expires = now() - timedelta(days=1)
self.order.save()
self.order.payments.create(
provider='manual',
@@ -2248,7 +2247,6 @@ class OrderChangeManagerTests(TestCase):
assert not self.order.fees.exists()
assert self.order.status == Order.STATUS_PAID
assert self.order.pending_sum == Decimal('0.00')
assert self.order.expires < now()
r = self.order.refunds.last()
assert r.provider == 'offsetting'
assert r.amount == Decimal('23.00')
@@ -2257,7 +2255,6 @@ class OrderChangeManagerTests(TestCase):
# New order
assert self.op2.order != self.order
o2 = self.op2.order
assert o2.expires > now()
assert o2.total == Decimal('42.00')
assert o2.status == Order.STATUS_PENDING
assert o2.positions.count() == 1

View File

@@ -22,7 +22,6 @@
import inspect
import pytest
from django.utils import translation
from django_scopes import scopes_disabled
from xdist.dsession import DSession
@@ -69,8 +68,3 @@ def pytest_fixture_setup(fixturedef, request):
else:
with scopes_disabled():
yield
@pytest.fixture(autouse=True)
def reset_locale():
translation.activate("en")

View File

@@ -20,19 +20,19 @@
# <https://www.gnu.org/licenses/>.
#
from django import urls
from django.test import override_settings
from django.conf import settings
from pretix.helpers.urls import build_absolute_uri
def test_site_url_domain():
with override_settings(SITE_URL='https://example.com'):
assert build_absolute_uri('control:auth.login') == 'https://example.com/control/login'
settings.SITE_URL = 'https://example.com'
assert build_absolute_uri('control:auth.login') == 'https://example.com/control/login'
def test_site_url_subpath():
with override_settings(SITE_URL='https://example.com/presale'):
old_prefix = urls.get_script_prefix()
urls.set_script_prefix('/presale/')
assert build_absolute_uri('control:auth.login') == 'https://example.com/presale/control/login'
urls.set_script_prefix(old_prefix)
settings.SITE_URL = 'https://example.com/presale'
old_prefix = urls.get_script_prefix()
urls.set_script_prefix('/presale/')
assert build_absolute_uri('control:auth.login') == 'https://example.com/presale/control/login'
urls.set_script_prefix(old_prefix)

View File

@@ -20,6 +20,7 @@
# <https://www.gnu.org/licenses/>.
#
import pytest
from django.conf import settings
from django.test.utils import override_settings
from django.utils.timezone import now
@@ -35,6 +36,7 @@ def env():
date_from=now(), live=True
)
event.get_cache().clear()
settings.SITE_URL = 'http://example.com'
return o, event

View File

@@ -20,6 +20,7 @@
# <https://www.gnu.org/licenses/>.
#
import pytest
from django.conf import settings
from django.utils.timezone import now
from pretix.base.models import Event, Organizer
@@ -32,6 +33,7 @@ def event():
organizer=o, name='MRMCD2015', slug='2015',
date_from=now(),
)
settings.SITE_URL = 'http://example.com'
return event

View File

@@ -20,6 +20,7 @@
# <https://www.gnu.org/licenses/>.
#
import pytest
from django.conf import settings
from django.template import Context, Template, TemplateSyntaxError
from django.urls import NoReverseMatch
from django.utils.timezone import now
@@ -35,6 +36,7 @@ def env():
organizer=o, name='MRMCD2015', slug='2015',
date_from=now()
)
settings.SITE_URL = 'http://example.com'
event.get_cache().clear()
return o, event

View File

@@ -20,6 +20,7 @@
# <https://www.gnu.org/licenses/>.
#
import pytest
from django.conf import settings
from django.test import override_settings
from django.utils.timezone import now
from django_scopes import scopes_disabled
@@ -37,6 +38,7 @@ def env():
organizer=o, name='MRMCD2015', slug='2015',
date_from=now()
)
settings.SITE_URL = 'http://example.com'
event.get_cache().clear()
return o, event
@@ -97,6 +99,7 @@ def test_event_org_domain_keep_port(env):
@pytest.mark.django_db
def test_event_org_domain_keep_scheme(env):
with override_settings(SITE_URL='https://example.com'):
settings.SITE_URL = 'https://example.com'
KnownDomain.objects.create(domainname='foobar', organizer=env[0])
assert eventreverse(env[1], 'presale:event.index') == 'https://foobar/2015/'

View File

@@ -1,91 +0,0 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
# This file is based on an earlier version of pretix which was released under the Apache License 2.0. The full text of
# the Apache License 2.0 can be obtained at <http://www.apache.org/licenses/LICENSE-2.0>.
#
# This file may have since been changed and any changes are released under the terms of AGPLv3 as described above. A
# full history of changes and contributors is available at <https://github.com/pretix/pretix>.
#
# This file contains Apache-licensed contributions copyrighted by: Flavia Bastos
#
# Unless required by applicable law or agreed to in writing, software distributed under the Apache License 2.0 is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under the License.
from django.core import mail as djmail
from django_countries.fields import Country
from django_scopes import scopes_disabled
from tests.presale.test_orders import BaseOrdersTest
from pretix.base.models import InvoiceAddress, OrderPayment
from pretix.base.services.invoices import generate_invoice
class BanktransferOrdersTest(BaseOrdersTest):
def test_unknown_order(self):
response = self.client.post(
'/%s/%s/banktransfer/ABCDE/123/mail-invoice/' % (self.orga.slug, self.event.slug)
)
assert response.status_code == 404
response = self.client.post(
'/%s/%s/banktransfer/%s/123/mail-invoice/' % (self.orga.slug, self.event.slug, self.not_my_order.code)
)
assert response.status_code == 404
def test_order_with_no_invoice(self):
djmail.outbox = []
response = self.client.post(
'/%s/%s/banktransfer/%s/%s/mail-invoice/' % (
self.orga.slug, self.event.slug, self.order.code, self.order.secret),
{'email': 'test@example.org'}
)
assert response.status_code == 302
from django.contrib.messages import get_messages
messages = list(get_messages(response.wsgi_request))
assert len(messages) == 1
assert str(messages[0]) == 'No pending bank transfer payment found. Maybe the order has been paid already?'
assert len(djmail.outbox) == 0
def test_valid_order(self):
with scopes_disabled():
self.event.settings.set('payment_banktransfer_invoice_email', True)
self.order.payments.create(provider='banktransfer', state=OrderPayment.PAYMENT_STATE_CREATED,
amount=self.order.total)
InvoiceAddress.objects.create(order=self.order, company="Sample company", country=Country('NZ'))
generate_invoice(self.order)
djmail.outbox = []
response = self.client.post(
'/%s/%s/banktransfer/%s/%s/mail-invoice/' % (
self.orga.slug, self.event.slug, self.order.code, self.order.secret),
{'email': 'test@example.org'}
)
assert response.status_code == 302
from django.contrib.messages import get_messages
messages = list(get_messages(response.wsgi_request))
assert len(messages) == 1
assert str(messages[0]) == 'Sending the latest invoice via e-mail to test@example.org.'
assert len(djmail.outbox) == 1

View File

@@ -582,6 +582,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
response = self.client.get('/%s/%s/widget/product_list' % (self.orga.slug, self.event.slug))
data = json.loads(response.content.decode())
settings.SITE_URL = 'http://example.com'
assert data == {
'list_type': 'list',
'name': '30C3',
@@ -609,6 +610,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
self.event.subevents.create(name="Hidden", active=True, is_public=False, date_from=now() + datetime.timedelta(days=3))
response = self.client.get('/%s/%s/widget/product_list?style=calendar' % (self.orga.slug, self.event.slug))
settings.SITE_URL = 'http://example.com'
data = json.loads(response.content.decode())
assert data == {
'list_type': 'calendar',
@@ -684,6 +686,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
self.event.subevents.create(name="Hidden", active=True, is_public=False, date_from=now() + datetime.timedelta(days=3))
response = self.client.get('/%s/%s/widget/product_list?style=week' % (self.orga.slug, self.event.slug))
settings.SITE_URL = 'http://example.com'
data = json.loads(response.content.decode())
assert data == {
'list_type': 'week',
@@ -727,6 +730,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
self.event.subevents.create(name="Disabled", active=False, date_from=now() + datetime.timedelta(days=3))
self.event.subevents.create(name="Hidden", active=True, is_public=False, date_from=now() + datetime.timedelta(days=3))
settings.SITE_URL = 'http://example.com'
response = self.client.get('/%s/widget/product_list' % (self.orga.slug,))
data = json.loads(response.content.decode())
assert data == {
@@ -769,6 +773,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
self.event.subevents.create(name="Hidden", active=True, is_public=False, date_from=now() + datetime.timedelta(days=3))
response = self.client.get('/%s/widget/product_list?style=calendar' % (self.orga.slug,))
settings.SITE_URL = 'http://example.com'
data = json.loads(response.content.decode())
assert data == {
'date': '2019-01-01',