mirror of
https://github.com/pretix/pretix.git
synced 2026-04-04 20:12:26 +00:00
Compare commits
2 Commits
exporter-a
...
position-a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc52dc81f3 | ||
|
|
f3dbbce39c |
@@ -76,7 +76,7 @@ dependencies = [
|
||||
"paypal-checkout-serversdk==1.0.*",
|
||||
"PyJWT==2.12.*",
|
||||
"phonenumberslite==9.0.*",
|
||||
"Pillow==12.2.*",
|
||||
"Pillow==12.1.*",
|
||||
"pretix-plugin-build",
|
||||
"protobuf==7.34.*",
|
||||
"psycopg2-binary",
|
||||
@@ -90,10 +90,10 @@ dependencies = [
|
||||
"pytz-deprecation-shim==0.1.*",
|
||||
"pyuca",
|
||||
"qrcode==8.2",
|
||||
"redis==7.4.*",
|
||||
"redis==7.1.*",
|
||||
"reportlab==4.4.*",
|
||||
"requests==2.32.*",
|
||||
"sentry-sdk==2.57.*",
|
||||
"sentry-sdk==2.56.*",
|
||||
"sepaxml==2.7.*",
|
||||
"stripe==7.9.*",
|
||||
"text-unidecode==1.*",
|
||||
|
||||
@@ -47,7 +47,6 @@ from django.utils.formats import localize
|
||||
from django.utils.translation import gettext, gettext_lazy as _
|
||||
|
||||
from pretix.base.models import Event
|
||||
from pretix.base.models.auth import PermissionHolder
|
||||
from pretix.helpers.safe_openpyxl import ( # NOQA: backwards compatibility for plugins using excel_safe
|
||||
SafeWorkbook, remove_invalid_excel_chars as excel_safe,
|
||||
)
|
||||
@@ -60,20 +59,11 @@ class BaseExporter:
|
||||
This is the base class for all data exporters
|
||||
"""
|
||||
|
||||
def __init__(self, event, organizer, permission_holder: PermissionHolder=None, progress_callback=lambda v: None):
|
||||
"""
|
||||
:param event: Event context, can also be a queryset of events for multi-event exports
|
||||
:param organizer: Organizer context
|
||||
:param user: The user who triggered the export (or None).
|
||||
:param token: The API token that triggered the export (or None).
|
||||
:param device: The device that triggered the export (or None)
|
||||
:param progress_callback: Callback function with progress
|
||||
"""
|
||||
def __init__(self, event, organizer, progress_callback=lambda v: None):
|
||||
self.event = event
|
||||
self.organizer = organizer
|
||||
self.progress_callback = progress_callback
|
||||
self.is_multievent = isinstance(event, QuerySet)
|
||||
self.permission_holder = permission_holder
|
||||
if isinstance(event, QuerySet):
|
||||
self.events = event
|
||||
self.event = None
|
||||
@@ -190,7 +180,7 @@ class BaseExporter:
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def get_required_event_permission(cls) -> Optional[str]:
|
||||
def get_required_event_permission(cls) -> str:
|
||||
"""
|
||||
The permission level required to use this exporter for events. For multi-event-exports, this will be used
|
||||
to limit the selection of events. Will be ignored if the ``OrganizerLevelExportMixin`` mixin is used.
|
||||
@@ -205,7 +195,7 @@ class OrganizerLevelExportMixin:
|
||||
raise TypeError("required_event_permission may not be called on OrganizerLevelExportMixin")
|
||||
|
||||
@classmethod
|
||||
def get_required_organizer_permission(cls) -> Optional[str]:
|
||||
def get_required_organizer_permission(cls) -> str:
|
||||
"""
|
||||
The permission level required to use this exporter. Must be set for organizer-level exports. Set to `None` to
|
||||
allow everyone with any access to the organizer.
|
||||
|
||||
@@ -38,7 +38,6 @@ import operator
|
||||
import secrets
|
||||
from datetime import timedelta
|
||||
from functools import reduce
|
||||
from typing import Protocol
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import (
|
||||
@@ -68,14 +67,6 @@ class EmailAddressTakenError(IntegrityError):
|
||||
pass
|
||||
|
||||
|
||||
class PermissionHolder(Protocol):
|
||||
def has_event_permission(self, organizer, event, perm_name=None, request=None, session_key=None) -> bool:
|
||||
...
|
||||
|
||||
def has_organizer_permission(self, organizer, perm_name=None, request=None):
|
||||
...
|
||||
|
||||
|
||||
class UserManager(BaseUserManager):
|
||||
"""
|
||||
This is the user manager for our custom user model. See the User
|
||||
|
||||
@@ -229,7 +229,7 @@ class Device(LoggedModel):
|
||||
"""
|
||||
return self._organizer_permission_set() if self.organizer == organizer else set()
|
||||
|
||||
def has_event_permission(self, organizer, event, perm_name=None, request=None, session_key=None) -> bool:
|
||||
def has_event_permission(self, organizer, event, perm_name=None, request=None) -> bool:
|
||||
"""
|
||||
Checks if this token is part of a team that grants access of type ``perm_name``
|
||||
to the event ``event``.
|
||||
@@ -238,7 +238,6 @@ class Device(LoggedModel):
|
||||
:param event: The event to check
|
||||
:param perm_name: The permission, e.g. ``event.orders:read``
|
||||
:param request: This parameter is ignored and only defined for compatibility reasons.
|
||||
:param session_key: This parameter is ignored and only defined for compatibility reasons.
|
||||
:return: bool
|
||||
"""
|
||||
has_event_access = (self.all_events and organizer == self.organizer) or (
|
||||
|
||||
@@ -319,9 +319,6 @@ class TeamQuerySet(models.QuerySet):
|
||||
def event_permission_q(cls, perm_name):
|
||||
from ..permissions import assert_valid_event_permission
|
||||
|
||||
if perm_name is None:
|
||||
return Q()
|
||||
|
||||
if perm_name.startswith('can_') and perm_name in OLD_TO_NEW_EVENT_COMPAT: # legacy
|
||||
return reduce(operator.and_, [cls.event_permission_q(p) for p in OLD_TO_NEW_EVENT_COMPAT[perm_name]])
|
||||
assert_valid_event_permission(perm_name, allow_legacy=False)
|
||||
@@ -334,9 +331,6 @@ class TeamQuerySet(models.QuerySet):
|
||||
def organizer_permission_q(cls, perm_name):
|
||||
from ..permissions import assert_valid_organizer_permission
|
||||
|
||||
if perm_name is None:
|
||||
return Q()
|
||||
|
||||
if perm_name.startswith('can_') and perm_name in OLD_TO_NEW_ORGANIZER_COMPAT: # legacy
|
||||
return reduce(operator.and_, [cls.organizer_permission_q(p) for p in OLD_TO_NEW_ORGANIZER_COMPAT[perm_name]])
|
||||
assert_valid_organizer_permission(perm_name, allow_legacy=False)
|
||||
@@ -556,7 +550,7 @@ class TeamAPIToken(models.Model):
|
||||
"""
|
||||
return self.team.organizer_permission_set() if self.team.organizer == organizer else set()
|
||||
|
||||
def has_event_permission(self, organizer, event, perm_name=None, request=None, session_key=None) -> bool:
|
||||
def has_event_permission(self, organizer, event, perm_name=None, request=None) -> bool:
|
||||
"""
|
||||
Checks if this token is part of a team that grants access of type ``perm_name``
|
||||
to the event ``event``.
|
||||
@@ -565,7 +559,6 @@ class TeamAPIToken(models.Model):
|
||||
:param event: The event to check
|
||||
:param perm_name: The permission, e.g. ``event.orders:read``
|
||||
:param request: This parameter is ignored and only defined for compatibility reasons.
|
||||
:param session_key: This parameter is ignored and only defined for compatibility reasons.
|
||||
:return: bool
|
||||
"""
|
||||
has_event_access = (self.team.all_events and organizer == self.team.organizer) or (
|
||||
|
||||
@@ -54,7 +54,7 @@ from bidi import get_display
|
||||
from django.conf import settings
|
||||
from django.contrib.staticfiles import finders
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db.models import Exists, Max, Min, OuterRef
|
||||
from django.db.models import Max, Min
|
||||
from django.db.models.fields.files import FieldFile
|
||||
from django.dispatch import receiver
|
||||
from django.utils.deconstruct import deconstructible
|
||||
@@ -76,7 +76,7 @@ from reportlab.pdfgen.canvas import Canvas
|
||||
from reportlab.platypus import Paragraph
|
||||
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.models import Checkin, Event, Order, OrderPosition, Question
|
||||
from pretix.base.models import Event, Order, OrderPosition, Question
|
||||
from pretix.base.settings import PERSON_NAME_SCHEMES
|
||||
from pretix.base.signals import layout_image_variables, layout_text_variables
|
||||
from pretix.base.templatetags.money import money_filter
|
||||
@@ -379,13 +379,6 @@ DEFAULT_VARIABLES = OrderedDict((
|
||||
str(p) for p in generate_compressed_addon_list(op, order, ev)
|
||||
])
|
||||
}),
|
||||
("checked_in_addons", {
|
||||
"label": _("List of Checked-In Add-Ons"),
|
||||
"editor_sample": _("Add-on 1\n2x Add-on 2"),
|
||||
"evaluate": lambda op, order, ev: "\n".join([
|
||||
str(p) for p in generate_compressed_addon_list(op, order, ev, only_checked_in=True)
|
||||
])
|
||||
}),
|
||||
("organizer", {
|
||||
"label": _("Organizer name"),
|
||||
"editor_sample": _("Event organizer company"),
|
||||
@@ -757,16 +750,12 @@ def get_program_times(op: OrderPosition, ev: Event):
|
||||
])
|
||||
|
||||
|
||||
def generate_compressed_addon_list(op, order, event, only_checked_in=False):
|
||||
def generate_compressed_addon_list(op, order, event):
|
||||
itemcount = defaultdict(int)
|
||||
addon_qs = (
|
||||
addons = [p for p in (
|
||||
op.addons.all() if 'addons' in getattr(op, '_prefetched_objects_cache', {})
|
||||
else op.addons.select_related('item', 'variation')
|
||||
)
|
||||
if only_checked_in:
|
||||
addon_qs = addon_qs.filter(Exists(Checkin.objects.filter(position=OuterRef('pk'))), canceled=False)
|
||||
addons = [p for p in addon_qs if not p.canceled]
|
||||
|
||||
) if not p.canceled]
|
||||
for pos in addons:
|
||||
itemcount[pos.item, pos.variation] += 1
|
||||
|
||||
|
||||
@@ -211,12 +211,7 @@ def init_event_exporters(event, user=None, token=None, device=None, request=None
|
||||
if not perm_holder.has_event_permission(event.organizer, event, permission_name, request) and not staff_session:
|
||||
continue
|
||||
|
||||
exporter: BaseExporter = response(
|
||||
event=event,
|
||||
organizer=event.organizer,
|
||||
permission_holder=token or device or user,
|
||||
**kwargs
|
||||
)
|
||||
exporter: BaseExporter = response(event=event, organizer=event.organizer, **kwargs)
|
||||
|
||||
if not exporter.available_for_user(user if user and user.is_authenticated else None):
|
||||
continue
|
||||
@@ -248,12 +243,7 @@ def init_organizer_exporters(
|
||||
continue
|
||||
|
||||
if issubclass(response, OrganizerLevelExportMixin):
|
||||
exporter: BaseExporter = response(
|
||||
event=Event.objects.none(),
|
||||
organizer=organizer,
|
||||
permission_holder=token or device or user,
|
||||
**kwargs,
|
||||
)
|
||||
exporter: BaseExporter = response(event=Event.objects.none(), organizer=organizer, **kwargs)
|
||||
|
||||
try:
|
||||
if not perm_holder.has_organizer_permission(organizer, response.get_required_organizer_permission(), request) and not staff_session:
|
||||
@@ -305,12 +295,7 @@ def init_organizer_exporters(
|
||||
if not _has_permission_on_any_team_cache[permission_name] and not staff_session:
|
||||
continue
|
||||
|
||||
exporter: BaseExporter = response(
|
||||
event=_event_list_cache[permission_name],
|
||||
organizer=organizer,
|
||||
permission_holder=token or device or user,
|
||||
**kwargs,
|
||||
)
|
||||
exporter: BaseExporter = response(event=_event_list_cache[permission_name], organizer=organizer, **kwargs)
|
||||
|
||||
if not exporter.available_for_user(user if user and user.is_authenticated else None):
|
||||
continue
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-03-30 11:22+0000\n"
|
||||
"PO-Revision-Date: 2026-03-31 17:00+0000\n"
|
||||
"PO-Revision-Date: 2026-03-18 12:23+0000\n"
|
||||
"Last-Translator: CVZ-es <damien.bremont@casadevelazquez.org>\n"
|
||||
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/pretix/"
|
||||
"es/>\n"
|
||||
@@ -13331,7 +13331,7 @@ msgstr ""
|
||||
#: pretix/base/settings.py:4157
|
||||
#, python-brace-format
|
||||
msgid "VAT-ID is not supported for \"{}\"."
|
||||
msgstr "El NIF no es compatible con «{}»."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/base/settings.py:4164
|
||||
msgid "The last payment date cannot be before the end of presale."
|
||||
@@ -27567,30 +27567,28 @@ msgid "Add a two-factor authentication device"
|
||||
msgstr "Añadir un dispositivo de autenticación de dos factores"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/user/2fa_add.html:19
|
||||
#, fuzzy
|
||||
#| msgid "Smartphone with the Authenticator application"
|
||||
msgid "Smartphone with Authenticator app"
|
||||
msgstr "Smartphone con la aplicación Authenticator"
|
||||
msgstr "Celular con aplicación de autenticación"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/user/2fa_add.html:21
|
||||
msgid ""
|
||||
"Use your smartphone with any Time-based One-Time-Password app like freeOTP, "
|
||||
"Google Authenticator or Proton Authenticator."
|
||||
msgstr ""
|
||||
"Use su smartphone con cualquier aplicación de contraseñas de un solo uso "
|
||||
"basadas en el tiempo, como freeOTP, Google Authenticator o Proton "
|
||||
"Authenticator."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/user/2fa_add.html:30
|
||||
#, fuzzy
|
||||
#| msgid "WebAuthn-compatible hardware token (e.g. Yubikey)"
|
||||
msgid "WebAuthn-compatible hardware token"
|
||||
msgstr "Token físico compatible con WebAuthn"
|
||||
msgstr "Hardware compatible con token WebAuthn (p. ej. Yubikey)"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/user/2fa_add.html:32
|
||||
msgid ""
|
||||
"Use a hardware token like the Yubikey, or other biometric authentication "
|
||||
"like fingerprint or face recognition."
|
||||
msgstr ""
|
||||
"Utiliza un dispositivo de seguridad físico, como el Yubikey, u otro método "
|
||||
"de autenticación biométrica, como el reconocimiento de huellas dactilares o "
|
||||
"facial."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/user/2fa_confirm_totp.html:8
|
||||
msgid "To set up this device, please follow the following steps:"
|
||||
|
||||
@@ -4,10 +4,10 @@ msgstr ""
|
||||
"Project-Id-Version: 1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-03-30 11:22+0000\n"
|
||||
"PO-Revision-Date: 2026-03-31 17:00+0000\n"
|
||||
"PO-Revision-Date: 2026-03-18 12:23+0000\n"
|
||||
"Last-Translator: CVZ-es <damien.bremont@casadevelazquez.org>\n"
|
||||
"Language-Team: French <https://translate.pretix.eu/projects/pretix/pretix/"
|
||||
"fr/>\n"
|
||||
"Language-Team: French <https://translate.pretix.eu/projects/pretix/pretix/fr/"
|
||||
">\n"
|
||||
"Language: fr\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -13454,7 +13454,7 @@ msgstr ""
|
||||
#: pretix/base/settings.py:4157
|
||||
#, python-brace-format
|
||||
msgid "VAT-ID is not supported for \"{}\"."
|
||||
msgstr "Le numéro de TVA n'est pas pris en charge pour « {} »."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/base/settings.py:4164
|
||||
msgid "The last payment date cannot be before the end of presale."
|
||||
@@ -27774,30 +27774,28 @@ msgid "Add a two-factor authentication device"
|
||||
msgstr "Ajouter un dispositif d'authentification à deux facteurs"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/user/2fa_add.html:19
|
||||
#, fuzzy
|
||||
#| msgid "Smartphone with the Authenticator application"
|
||||
msgid "Smartphone with Authenticator app"
|
||||
msgstr "Smartphone équipé de l'application Authenticator"
|
||||
msgstr "Smartphone avec l'application Authenticator"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/user/2fa_add.html:21
|
||||
msgid ""
|
||||
"Use your smartphone with any Time-based One-Time-Password app like freeOTP, "
|
||||
"Google Authenticator or Proton Authenticator."
|
||||
msgstr ""
|
||||
"Utilisez votre smartphone avec n'importe quelle application de mots de passe "
|
||||
"à usage unique générés en temps réel, comme freeOTP, Google Authenticator ou "
|
||||
"Proton Authenticator."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/user/2fa_add.html:30
|
||||
#, fuzzy
|
||||
#| msgid "WebAuthn-compatible hardware token (e.g. Yubikey)"
|
||||
msgid "WebAuthn-compatible hardware token"
|
||||
msgstr "Token matériel compatible WebAuthn"
|
||||
msgstr "Token matériel compatible WebAuthn (par ex. Yubikey)"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/user/2fa_add.html:32
|
||||
msgid ""
|
||||
"Use a hardware token like the Yubikey, or other biometric authentication "
|
||||
"like fingerprint or face recognition."
|
||||
msgstr ""
|
||||
"Utilisez une clé matérielle telle que la Yubikey, ou un autre moyen "
|
||||
"d'authentification biométrique, comme la reconnaissance d'empreintes "
|
||||
"digitales ou faciale."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/user/2fa_confirm_totp.html:8
|
||||
msgid "To set up this device, please follow the following steps:"
|
||||
|
||||
@@ -7,10 +7,10 @@ msgstr ""
|
||||
"Project-Id-Version: 1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-03-30 11:22+0000\n"
|
||||
"PO-Revision-Date: 2026-03-31 17:00+0000\n"
|
||||
"PO-Revision-Date: 2026-03-18 12:23+0000\n"
|
||||
"Last-Translator: Ruud Hendrickx <ruud@leckxicon.eu>\n"
|
||||
"Language-Team: Dutch <https://translate.pretix.eu/projects/pretix/pretix/nl/>"
|
||||
"\n"
|
||||
"Language-Team: Dutch <https://translate.pretix.eu/projects/pretix/pretix/nl/"
|
||||
">\n"
|
||||
"Language: nl\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -13283,7 +13283,7 @@ msgstr ""
|
||||
#: pretix/base/settings.py:4157
|
||||
#, python-brace-format
|
||||
msgid "VAT-ID is not supported for \"{}\"."
|
||||
msgstr "Btw-nummer wordt niet ondersteund voor \"{}\"."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/base/settings.py:4164
|
||||
msgid "The last payment date cannot be before the end of presale."
|
||||
@@ -27461,28 +27461,28 @@ msgid "Add a two-factor authentication device"
|
||||
msgstr "Twee-factor-authenticatieapparaat toevoegen"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/user/2fa_add.html:19
|
||||
#, fuzzy
|
||||
#| msgid "Smartphone with the Authenticator application"
|
||||
msgid "Smartphone with Authenticator app"
|
||||
msgstr "Smartphone met Authenticator-app"
|
||||
msgstr "Smartphone met de Authenticator-applicatie"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/user/2fa_add.html:21
|
||||
msgid ""
|
||||
"Use your smartphone with any Time-based One-Time-Password app like freeOTP, "
|
||||
"Google Authenticator or Proton Authenticator."
|
||||
msgstr ""
|
||||
"Gebruik uw smartphone met een willekeurige app voor tijdgebonden eenmalige "
|
||||
"wachtwoorden, zoals freeOTP, Google Authenticator of Proton Authenticator."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/user/2fa_add.html:30
|
||||
#, fuzzy
|
||||
#| msgid "WebAuthn-compatible hardware token (e.g. Yubikey)"
|
||||
msgid "WebAuthn-compatible hardware token"
|
||||
msgstr "WebAuthn-compatibel hardwaretoken"
|
||||
msgstr "WebAuthn-compatibel hardware-token (bijvoorbeeld Yubikey)"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/user/2fa_add.html:32
|
||||
msgid ""
|
||||
"Use a hardware token like the Yubikey, or other biometric authentication "
|
||||
"like fingerprint or face recognition."
|
||||
msgstr ""
|
||||
"Gebruik een hardwaretoken zoals de Yubikey, of een andere vorm van "
|
||||
"biometrische authenticatie, zoals vingerafdruk- of gezichtsherkenning."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/user/2fa_confirm_totp.html:8
|
||||
msgid "To set up this device, please follow the following steps:"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-03-30 11:22+0000\n"
|
||||
"PO-Revision-Date: 2026-03-31 17:00+0000\n"
|
||||
"PO-Revision-Date: 2026-03-18 14:50+0000\n"
|
||||
"Last-Translator: Ruud Hendrickx <ruud@leckxicon.eu>\n"
|
||||
"Language-Team: Dutch (informal) <https://translate.pretix.eu/projects/pretix/"
|
||||
"pretix/nl_Informal/>\n"
|
||||
@@ -13314,7 +13314,7 @@ msgstr ""
|
||||
#: pretix/base/settings.py:4157
|
||||
#, python-brace-format
|
||||
msgid "VAT-ID is not supported for \"{}\"."
|
||||
msgstr "Btw-nummer wordt niet ondersteund voor \"{}\"."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/base/settings.py:4164
|
||||
msgid "The last payment date cannot be before the end of presale."
|
||||
@@ -27518,28 +27518,28 @@ msgid "Add a two-factor authentication device"
|
||||
msgstr "Twee-factor-authenticatieapparaat toevoegen"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/user/2fa_add.html:19
|
||||
#, fuzzy
|
||||
#| msgid "Smartphone with the Authenticator application"
|
||||
msgid "Smartphone with Authenticator app"
|
||||
msgstr "Smartphone met Authenticator-app"
|
||||
msgstr "Smartphone met de Authenticator-applicatie"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/user/2fa_add.html:21
|
||||
msgid ""
|
||||
"Use your smartphone with any Time-based One-Time-Password app like freeOTP, "
|
||||
"Google Authenticator or Proton Authenticator."
|
||||
msgstr ""
|
||||
"Gebruik je smartphone met een willekeurige app voor tijdgebonden eenmalige "
|
||||
"wachtwoorden, zoals freeOTP, Google Authenticator of Proton Authenticator."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/user/2fa_add.html:30
|
||||
#, fuzzy
|
||||
#| msgid "WebAuthn-compatible hardware token (e.g. Yubikey)"
|
||||
msgid "WebAuthn-compatible hardware token"
|
||||
msgstr "WebAuthn-compatibel hardwaretoken"
|
||||
msgstr "WebAuthn-compatibel hardware-token (bijvoorbeeld Yubikey)"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/user/2fa_add.html:32
|
||||
msgid ""
|
||||
"Use a hardware token like the Yubikey, or other biometric authentication "
|
||||
"like fingerprint or face recognition."
|
||||
msgstr ""
|
||||
"Gebruik een hardwaretoken zoals de Yubikey, of een andere vorm van "
|
||||
"biometrische authenticatie, zoals vingerafdruk- of gezichtsherkenning."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/user/2fa_confirm_totp.html:8
|
||||
msgid "To set up this device, please follow the following steps:"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-03-30 11:22+0000\n"
|
||||
"PO-Revision-Date: 2026-03-30 21:00+0000\n"
|
||||
"PO-Revision-Date: 2026-03-25 08:00+0000\n"
|
||||
"Last-Translator: Renne Rocha <renne@rocha.dev.br>\n"
|
||||
"Language-Team: Portuguese (Brazil) <https://translate.pretix.eu/projects/"
|
||||
"pretix/pretix/pt_BR/>\n"
|
||||
@@ -19613,7 +19613,7 @@ msgstr "Excluir"
|
||||
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/import_form.html:91
|
||||
#: pretix/presale/templates/pretixpresale/fragment_event_list_filter.html:22
|
||||
msgid "Filter"
|
||||
msgstr "Filtrar"
|
||||
msgstr "Filtro"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/checkin/checkins.html:50
|
||||
msgid "Your search did not match any check-ins."
|
||||
@@ -28003,9 +28003,6 @@ msgid ""
|
||||
"According to your event settings, sold out products are hidden from "
|
||||
"customers. This way, customers will not be able to discover the waiting list."
|
||||
msgstr ""
|
||||
"De acordo com as configurações do seu evento, os produtos esgotados ficam "
|
||||
"ocultos para os clientes. Dessa forma, os clientes não poderão descobrir a "
|
||||
"lista de espera."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:38
|
||||
msgid "Send vouchers"
|
||||
@@ -28052,9 +28049,6 @@ msgid ""
|
||||
"waiting list in, you could sell tickets worth an additional "
|
||||
"<strong>%(amount)s</strong>."
|
||||
msgstr ""
|
||||
"Se você conseguir criar espaço suficiente em seu evento para acomodar todas "
|
||||
"as pessoas na lista de espera, poderá vender ingressos no valor de um "
|
||||
"adicional de <strong>%(amount)s</strong>."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:115
|
||||
msgid "Successfully redeemed"
|
||||
|
||||
@@ -296,8 +296,7 @@ class SetPasswordForm(forms.Form):
|
||||
}
|
||||
email = forms.EmailField(
|
||||
label=_('Email'),
|
||||
widget=forms.EmailInput(attrs={'autocomplete': 'username', 'readonly': 'readonly'}),
|
||||
required=False,
|
||||
disabled=True
|
||||
)
|
||||
password = forms.CharField(
|
||||
label=_('Password'),
|
||||
|
||||
@@ -70,21 +70,18 @@ def cached_invoice_address(request):
|
||||
# do not create a session, if we don't have a session we also don't have an invoice address ;)
|
||||
request._checkout_flow_invoice_address = InvoiceAddress()
|
||||
return request._checkout_flow_invoice_address
|
||||
cs = cart_session(request, create=False)
|
||||
if cs is None:
|
||||
cs = cart_session(request)
|
||||
iapk = cs.get('invoice_address')
|
||||
if not iapk:
|
||||
request._checkout_flow_invoice_address = InvoiceAddress()
|
||||
else:
|
||||
iapk = cs.get('invoice_address')
|
||||
if not iapk:
|
||||
try:
|
||||
with scopes_disabled():
|
||||
request._checkout_flow_invoice_address = InvoiceAddress.objects.get(
|
||||
pk=iapk, order__isnull=True
|
||||
)
|
||||
except InvoiceAddress.DoesNotExist:
|
||||
request._checkout_flow_invoice_address = InvoiceAddress()
|
||||
else:
|
||||
try:
|
||||
with scopes_disabled():
|
||||
request._checkout_flow_invoice_address = InvoiceAddress.objects.get(
|
||||
pk=iapk, order__isnull=True
|
||||
)
|
||||
except InvoiceAddress.DoesNotExist:
|
||||
request._checkout_flow_invoice_address = InvoiceAddress()
|
||||
return request._checkout_flow_invoice_address
|
||||
|
||||
|
||||
@@ -114,12 +111,6 @@ class CartMixin:
|
||||
return cached_invoice_address(self.request)
|
||||
|
||||
def get_cart(self, answers=False, queryset=None, order=None, downloads=False, payments=None):
|
||||
if not self.request.session.session_key and not order:
|
||||
# The user has not even a session ID yet, so they can't have a cart and we can save a lot of work
|
||||
return {
|
||||
'positions': [],
|
||||
# Other keys are not used on non-checkout pages
|
||||
}
|
||||
if queryset is not None:
|
||||
prefetch = []
|
||||
if answers:
|
||||
@@ -175,8 +166,7 @@ class CartMixin:
|
||||
else:
|
||||
fees = []
|
||||
|
||||
if not order and lcp:
|
||||
# Do not re-round for empty cart (useless) or confirmed order (incorrect)
|
||||
if not order:
|
||||
apply_rounding(self.request.event.settings.tax_rounding, self.invoice_address, self.request.event.currency, [*lcp, *fees])
|
||||
|
||||
total = sum([c.price for c in lcp]) + sum([f.value for f in fees])
|
||||
@@ -287,12 +277,6 @@ class CartMixin:
|
||||
}
|
||||
|
||||
def current_selected_payments(self, positions, fees, invoice_address, *, warn=False):
|
||||
from pretix.presale.views.cart import get_or_create_cart_id
|
||||
|
||||
if not get_or_create_cart_id(self.request, create=False):
|
||||
# No active cart ID, no payments there
|
||||
return []
|
||||
|
||||
raw_payments = copy.deepcopy(self.cart_session.get('payments', []))
|
||||
fees = [f for f in fees if f.fee_type != OrderFee.FEE_TYPE_PAYMENT] # we re-compute these here
|
||||
|
||||
|
||||
@@ -417,7 +417,7 @@ def get_or_create_cart_id(request, create=True):
|
||||
return new_id
|
||||
|
||||
|
||||
def cart_session(request, create=True):
|
||||
def cart_session(request):
|
||||
"""
|
||||
Before pretix 1.8.0, all checkout-related information (like the entered email address) was stored
|
||||
in the user's regular session dictionary. This led to data interference and leaks for example if a
|
||||
@@ -428,9 +428,7 @@ def cart_session(request, create=True):
|
||||
active cart session sub-dictionary for read and write access.
|
||||
"""
|
||||
request.session.modified = True
|
||||
cart_id = get_or_create_cart_id(request, create=create)
|
||||
if not cart_id and not create:
|
||||
return None
|
||||
cart_id = get_or_create_cart_id(request)
|
||||
return request.session['carts'][cart_id]
|
||||
|
||||
|
||||
|
||||
@@ -157,7 +157,7 @@ DATABASES = {
|
||||
'HOST': config.get('database', 'host', fallback=''),
|
||||
'PORT': config.get('database', 'port', fallback=''),
|
||||
'CONN_MAX_AGE': 0 if db_backend == 'sqlite3' else 120,
|
||||
'CONN_HEALTH_CHECKS': db_backend != 'sqlite3',
|
||||
'CONN_HEALTH_CHECKS': db_backend != 'sqlite3', # Will only be used from Django 4.1 onwards
|
||||
'DISABLE_SERVER_SIDE_CURSORS': db_disable_server_side_cursors,
|
||||
'OPTIONS': db_options,
|
||||
'TEST': {}
|
||||
@@ -179,21 +179,6 @@ if config.has_section('replica'):
|
||||
}
|
||||
DATABASE_ROUTERS = ['pretix.helpers.database.ReplicaRouter']
|
||||
|
||||
if config.has_section('dbreadonly'):
|
||||
DATABASES['readonly'] = {
|
||||
'ENGINE': 'django.db.backends.' + db_backend,
|
||||
'NAME': config.get('dbreadonly', 'name', fallback=DATABASES['default']['NAME']),
|
||||
'USER': config.get('dbreadonly', 'user', fallback=DATABASES['default']['USER']),
|
||||
'PASSWORD': config.get('dbreadonly', 'password', fallback=DATABASES['default']['PASSWORD']),
|
||||
'HOST': config.get('dbreadonly', 'host', fallback=DATABASES['default']['HOST']),
|
||||
'PORT': config.get('dbreadonly', 'port', fallback=DATABASES['default']['PORT']),
|
||||
'CONN_MAX_AGE': 0, # do not spam primary with open connections as long as readonly is only used occasionally
|
||||
'CONN_HEALTH_CHECKS': db_backend != 'sqlite3',
|
||||
'DISABLE_SERVER_SIDE_CURSORS': db_disable_server_side_cursors,
|
||||
'OPTIONS': db_options,
|
||||
'TEST': {}
|
||||
}
|
||||
|
||||
STATIC_URL = config.get('urls', 'static', fallback='/static/')
|
||||
|
||||
MEDIA_URL = config.get('urls', 'media', fallback='/media/')
|
||||
|
||||
@@ -2053,7 +2053,7 @@ def test_pdf_data(token_client, organizer, event, order, django_assert_max_num_q
|
||||
assert not resp.data['positions'][0].get('pdf_data')
|
||||
|
||||
# order list
|
||||
with django_assert_max_num_queries(34):
|
||||
with django_assert_max_num_queries(33):
|
||||
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?pdf_data=true'.format(
|
||||
organizer.slug, event.slug
|
||||
))
|
||||
@@ -2068,7 +2068,7 @@ def test_pdf_data(token_client, organizer, event, order, django_assert_max_num_q
|
||||
assert not resp.data['results'][0]['positions'][0].get('pdf_data')
|
||||
|
||||
# position list
|
||||
with django_assert_max_num_queries(36):
|
||||
with django_assert_max_num_queries(35):
|
||||
resp = token_client.get('/api/v1/organizers/{}/events/{}/orderpositions/?pdf_data=true'.format(
|
||||
organizer.slug, event.slug
|
||||
))
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
import datetime
|
||||
import re
|
||||
from decimal import Decimal
|
||||
from importlib import import_module
|
||||
from json import loads
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
@@ -81,34 +80,6 @@ class EventMiddlewareTest(EventTestMixin, SoupTest):
|
||||
doc = self.get_doc('/%s/%s/' % (self.orga.slug, self.event.slug))
|
||||
self.assertIn(str(self.event.name), doc.find("h1").text)
|
||||
|
||||
def test_no_session_cookie_set_on_event_index_view(self):
|
||||
resp = self.client.get('/%s/%s/' % (self.orga.slug, self.event.slug))
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
assert settings.SESSION_COOKIE_NAME not in self.client.cookies
|
||||
|
||||
def test_no_cart_session_added_on_event_index_view(self):
|
||||
# Make sure a session is present by doing a cart op on another event
|
||||
event2 = Event.objects.create(
|
||||
organizer=self.orga, name='30C3b', slug='30c3b',
|
||||
date_from=datetime.datetime(now().year + 1, 12, 26, 14, 0, tzinfo=datetime.timezone.utc),
|
||||
live=True,
|
||||
)
|
||||
self.client.post('/%s/%s/cart/add' % (self.orga.slug, event2.slug), {
|
||||
'item_%d' % 1337: '1', # item does not need to exist
|
||||
'ajax': 1
|
||||
})
|
||||
assert settings.SESSION_COOKIE_NAME in self.client.cookies
|
||||
|
||||
# Visit shop, make sure no session is created
|
||||
resp = self.client.get('/%s/%s/' % (self.orga.slug, self.event.slug))
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
SessionStore = import_module(settings.SESSION_ENGINE).SessionStore
|
||||
session = SessionStore(self.client.cookies[settings.SESSION_COOKIE_NAME].value).load()
|
||||
assert set(session.keys()) == {
|
||||
f"current_cart_event_{event2.pk}", "carts"
|
||||
}
|
||||
|
||||
def test_not_found(self):
|
||||
resp = self.client.get('/%s/%s/' % ('foo', 'bar'))
|
||||
self.assertEqual(resp.status_code, 404)
|
||||
|
||||
Reference in New Issue
Block a user