mirror of
https://github.com/pretix/pretix.git
synced 2026-07-02 04:41:55 +00:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 67f7fec134 | |||
| c2c97f31ca | |||
| fd565ecdb2 | |||
| f35b13b686 | |||
| 550bb675f5 | |||
| adc9c9d514 | |||
| 8441c4bc7a | |||
| 97ff252c09 | |||
| d3ca2ac1e5 | |||
| 6eebaaa563 | |||
| c9781f012b | |||
| 000bf54105 | |||
| e42d3d632f | |||
| 3bf5a5e478 | |||
| a6f31df0d4 |
@@ -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__ = "2026.6.0.dev0"
|
||||
__version__ = "2026.7.0.dev0"
|
||||
|
||||
@@ -53,7 +53,6 @@ from django.db.models import QuerySet
|
||||
from django.forms import Select, widgets
|
||||
from django.forms.widgets import FILE_INPUT_CONTRADICTION
|
||||
from django.utils.formats import date_format
|
||||
from django.utils.functional import lazy
|
||||
from django.utils.html import escape
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.text import format_lazy
|
||||
@@ -325,21 +324,16 @@ class WrappedPhonePrefixSelect(Select):
|
||||
initial = None
|
||||
|
||||
def __init__(self, initial=None):
|
||||
def _get_choices():
|
||||
choices = [("", "---------")]
|
||||
|
||||
if initial:
|
||||
for prefix, values in COUNTRY_CODE_TO_REGION_CODE.items():
|
||||
if all(v == REGION_CODE_FOR_NON_GEO_ENTITY for v in values):
|
||||
continue
|
||||
if initial in values:
|
||||
self.initial = "+%d" % prefix
|
||||
break
|
||||
choices += get_phone_prefixes_sorted_and_localized()
|
||||
return choices
|
||||
|
||||
choices = lazy(_get_choices, list)()
|
||||
choices = [("", "---------")]
|
||||
|
||||
if initial:
|
||||
for prefix, values in COUNTRY_CODE_TO_REGION_CODE.items():
|
||||
if all(v == REGION_CODE_FOR_NON_GEO_ENTITY for v in values):
|
||||
continue
|
||||
if initial in values:
|
||||
self.initial = "+%d" % prefix
|
||||
break
|
||||
choices += get_phone_prefixes_sorted_and_localized()
|
||||
super().__init__(choices=choices, attrs={
|
||||
'aria-label': pgettext_lazy('phonenumber', 'International area code'),
|
||||
'autocomplete': 'tel-country-code',
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
#
|
||||
# This file is part of pretix (Community Edition).
|
||||
#
|
||||
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||
# Copyright (C) 2020-today pretix 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/>.
|
||||
#
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Do nothing. Useful for startup performance testing."
|
||||
|
||||
def handle(self, *args, **options):
|
||||
pass
|
||||
@@ -40,6 +40,7 @@ import warnings
|
||||
from collections import Counter, OrderedDict, defaultdict
|
||||
from datetime import datetime, time, timedelta
|
||||
from operator import attrgetter
|
||||
from typing import TYPE_CHECKING
|
||||
from urllib.parse import urljoin
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
@@ -79,10 +80,16 @@ from pretix.helpers.thumb import get_thumbnail
|
||||
from ..settings import settings_hierarkey
|
||||
from .organizer import Organizer, Team
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from hierarkey.proxy import HierarkeyProxy
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class EventMixin:
|
||||
if TYPE_CHECKING:
|
||||
settings: HierarkeyProxy
|
||||
|
||||
def clean(self):
|
||||
if self.presale_start and self.presale_end and self.presale_start > self.presale_end:
|
||||
raise ValidationError({'presale_end': _('The end of the presale period has to be later than its start.')})
|
||||
@@ -899,7 +906,7 @@ class Event(EventMixin, LoggedModel):
|
||||
self.save()
|
||||
self.log_action('pretix.object.cloned', data={'source': other.slug, 'source_id': other.pk})
|
||||
|
||||
if hasattr(other, 'alternative_domain_assignment'):
|
||||
if hasattr(other, 'alternative_domain_assignment') and not is_cross_organizer:
|
||||
other.alternative_domain_assignment.domain.event_assignments.create(event=self)
|
||||
|
||||
if not self.all_sales_channels:
|
||||
|
||||
@@ -35,6 +35,7 @@ import operator
|
||||
import string
|
||||
from datetime import date, datetime, time
|
||||
from functools import reduce
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import pytz_deprecation_shim
|
||||
from django.conf import settings
|
||||
@@ -61,6 +62,9 @@ from ...helpers.permission_migration import (
|
||||
from ..settings import settings_hierarkey
|
||||
from .auth import User
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from hierarkey.proxy import HierarkeyProxy
|
||||
|
||||
|
||||
@settings_hierarkey.add(cache_namespace='organizer')
|
||||
class Organizer(LoggedModel):
|
||||
@@ -78,6 +82,9 @@ class Organizer(LoggedModel):
|
||||
"""
|
||||
|
||||
settings_namespace = 'organizer'
|
||||
if TYPE_CHECKING:
|
||||
settings: HierarkeyProxy
|
||||
|
||||
name = models.CharField(max_length=200,
|
||||
verbose_name=_("Name"))
|
||||
slug = models.CharField(
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
{% extends "error.html" %}
|
||||
{% load i18n %}
|
||||
{% load eventurl %}
|
||||
{% load urlreplace %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% trans "Please continue in a new tab" %}</h1>
|
||||
<p class="larger">
|
||||
{% blocktrans trimmed %}
|
||||
For security reasons, the following step is only possible in a new tab.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<p class="larger">
|
||||
{% blocktrans trimmed %}
|
||||
If the new tab did not open automatically, please click the following button:
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<div class="text-center">
|
||||
<a href="{{ url }}"
|
||||
class="btn btn-primary btn-lg" target="_blank">
|
||||
<span class="fa fa-external-link-square"></span>
|
||||
{% trans "Continue in new tab" %}
|
||||
</a>
|
||||
{{ url|json_script:"framebreak-url" }}
|
||||
<script type="text/javascript" src="{% static "pretixbase/js/framebreak.js" %}"></script>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -42,8 +42,6 @@ from bleach import DEFAULT_CALLBACKS, html5lib_shim
|
||||
from bleach.linkifier import build_email_re
|
||||
from django import template
|
||||
from django.conf import settings
|
||||
from django.core import signing
|
||||
from django.urls import reverse
|
||||
from django.utils.functional import SimpleLazyObject
|
||||
from django.utils.html import escape
|
||||
from django.utils.http import url_has_allowed_host_and_scheme
|
||||
@@ -54,6 +52,7 @@ from markdown.postprocessors import Postprocessor
|
||||
from markdown.treeprocessors import UnescapeTreeprocessor
|
||||
from tlds import tld_set
|
||||
|
||||
from pretix.base.views.redirect import safelink
|
||||
from pretix.helpers.format import SafeFormatter, format_map
|
||||
|
||||
register = template.Library()
|
||||
@@ -158,8 +157,7 @@ def safelink_callback(attrs, new=False):
|
||||
"""
|
||||
url = html.unescape(attrs.get((None, 'href'), '/'))
|
||||
if not url_has_allowed_host_and_scheme(url, allowed_hosts=None) and not url.startswith('mailto:') and not url.startswith('tel:'):
|
||||
signer = signing.Signer(salt='safe-redirect')
|
||||
attrs[None, 'href'] = reverse('redirect') + '?url=' + urllib.parse.quote(signer.sign(url))
|
||||
attrs[None, 'href'] = safelink(url)
|
||||
attrs[None, 'target'] = '_blank'
|
||||
attrs[None, 'rel'] = 'noopener'
|
||||
return attrs
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
# 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/>.
|
||||
#
|
||||
import logging
|
||||
import urllib.parse
|
||||
|
||||
from django.core import signing
|
||||
@@ -26,6 +27,8 @@ from django.http import HttpResponseBadRequest, HttpResponseRedirect
|
||||
from django.shortcuts import render
|
||||
from django.urls import reverse
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _is_samesite_referer(request):
|
||||
referer = request.headers.get('referer')
|
||||
@@ -42,11 +45,16 @@ def _is_samesite_referer(request):
|
||||
|
||||
|
||||
def redir_view(request):
|
||||
signer = signing.Signer(salt='safe-redirect')
|
||||
framebreak = "framebreak" in request.GET
|
||||
salt = 'framebreak-safelink-url' if framebreak else 'safelink-url'
|
||||
try:
|
||||
url = signer.unsign(request.GET.get('url', ''))
|
||||
url = signing.Signer(salt=salt).unsign(request.GET.get('url', ''))
|
||||
except signing.BadSignature:
|
||||
return HttpResponseBadRequest('Invalid parameter')
|
||||
try:
|
||||
# Backwards-compatibility for a change in 2026-06, remove after a while
|
||||
url = signing.Signer(salt='safe-redirect').unsign(request.GET.get('url', ''))
|
||||
except signing.BadSignature:
|
||||
return HttpResponseBadRequest('Invalid parameter')
|
||||
|
||||
if not _is_samesite_referer(request):
|
||||
u = urllib.parse.urlparse(url)
|
||||
@@ -55,11 +63,26 @@ def redir_view(request):
|
||||
'url': url,
|
||||
})
|
||||
|
||||
if framebreak:
|
||||
r = render(request, 'pretixbase/framebreak.html', {
|
||||
'url': url,
|
||||
})
|
||||
r.xframe_options_exempt = True
|
||||
return r
|
||||
|
||||
r = HttpResponseRedirect(url)
|
||||
r['X-Robots-Tag'] = 'noindex'
|
||||
return r
|
||||
|
||||
|
||||
def safelink(url):
|
||||
signer = signing.Signer(salt='safe-redirect')
|
||||
return reverse('redirect') + '?url=' + urllib.parse.quote(signer.sign(url))
|
||||
def safelink(url, framebreak=False):
|
||||
url = str(url)
|
||||
if not (url.startswith('https://') or url.startswith('http://') or url.startswith("/")):
|
||||
logger.warning('Invalid URL passed to safelink: %r', url)
|
||||
return '#invalid-url'
|
||||
salt = 'framebreak-safelink-url' if framebreak else 'safelink-url'
|
||||
signer = signing.Signer(salt=salt)
|
||||
u = reverse('redirect') + '?url=' + urllib.parse.quote(signer.sign(url))
|
||||
if framebreak:
|
||||
u += "&framebreak=true"
|
||||
return u
|
||||
|
||||
@@ -1673,7 +1673,7 @@ class CountriesAndEUAndStates(CountriesAndEU):
|
||||
|
||||
class TaxRuleLineForm(I18nForm):
|
||||
country = LazyTypedChoiceField(
|
||||
choices=lazy(lambda: CountriesAndEUAndStates(), CountriesAndEUAndStates),
|
||||
choices=CountriesAndEUAndStates(),
|
||||
required=False
|
||||
)
|
||||
address_type = forms.ChoiceField(
|
||||
|
||||
@@ -212,7 +212,7 @@ class AuditLogMiddleware:
|
||||
if request.path.startswith(get_script_prefix() + 'control') and request.user.is_authenticated:
|
||||
if getattr(request.user, "is_hijacked", False):
|
||||
hijack_history = request.session.get('hijack_history', False)
|
||||
hijacker = get_object_or_404(User, pk=hijack_history[0])
|
||||
hijacker = get_object_or_404(User, pk=hijack_history[0]["user"])
|
||||
ss = hijacker.get_active_staff_session(request.session.get('hijacker_session'))
|
||||
if ss:
|
||||
ss.logs.create(
|
||||
|
||||
@@ -19,19 +19,22 @@
|
||||
# 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/>.
|
||||
#
|
||||
import hmac
|
||||
import json
|
||||
from contextlib import contextmanager
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth import (
|
||||
BACKEND_SESSION_KEY, get_user_model, load_backend, login,
|
||||
BACKEND_SESSION_KEY, HASH_SESSION_KEY, get_user_model, load_backend, login,
|
||||
logout,
|
||||
)
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.auth.views import redirect_to_login
|
||||
from django.db import transaction
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.urls import reverse
|
||||
from django.utils.crypto import get_random_string
|
||||
from django.utils.crypto import get_random_string, salted_hmac
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views import View
|
||||
@@ -230,7 +233,15 @@ class UserImpersonateView(AdministratorPermissionRequiredMixin, RecentAuthentica
|
||||
hijacked = self.object
|
||||
|
||||
hijack_history = request.session.get("hijack_history", [])
|
||||
hijack_history.append(request.user._meta.pk.value_to_string(hijacker))
|
||||
hijack_history.append({
|
||||
"user": request.user.pk,
|
||||
# We include the auth_hash, because it is unguessable. So should an attacker gain an attack vector to
|
||||
# modify hijack_history, they can't just insert or change a user that shouldn't be there. We HMAC it
|
||||
# again, though, since we also do not want the auth_hash of the admin user to be in the session of an
|
||||
# unprivileged user to contain the risk if there is some leak of session data.
|
||||
"auth_hash": salted_hmac(key_salt=b"hijack-history-hash", value=request.session[HASH_SESSION_KEY],
|
||||
algorithm="sha256", secret=settings.SECRET_KEY).hexdigest(),
|
||||
})
|
||||
|
||||
backend = get_used_backend(request)
|
||||
backend = f"{backend.__module__}.{backend.__class__.__name__}"
|
||||
@@ -259,8 +270,21 @@ class UserImpersonateStopView(LoginRequiredMixin, View):
|
||||
hijs = request.session['hijacker_session']
|
||||
hijack_history = request.session.get("hijack_history", [])
|
||||
hijacked = request.user
|
||||
user_pk = hijack_history.pop()
|
||||
hijacker = get_object_or_404(get_user_model(), pk=user_pk)
|
||||
prev_session = hijack_history.pop()
|
||||
hijacker = get_object_or_404(get_user_model(), pk=prev_session["user"])
|
||||
|
||||
expected_hash = salted_hmac(
|
||||
key_salt=b"hijack-history-hash",
|
||||
value=hijacker.get_session_auth_hash(),
|
||||
algorithm="sha256",
|
||||
secret=settings.SECRET_KEY
|
||||
).hexdigest()
|
||||
if not hmac.compare_digest(expected_hash, prev_session["auth_hash"]):
|
||||
# Could be an attacker-controlled hijack history, but could also be e.g. a password change of the admin user
|
||||
# that happened during the hijack session
|
||||
logout(request)
|
||||
return redirect_to_login(request.get_full_path())
|
||||
|
||||
backend = get_used_backend(request)
|
||||
backend = f"{backend.__module__}.{backend.__class__.__name__}"
|
||||
with signals.no_update_last_login(), keep_session_age(request.session):
|
||||
|
||||
@@ -46,6 +46,13 @@ class RequestIdFilter(logging.Filter):
|
||||
return True
|
||||
|
||||
|
||||
class SkipNotFoundFilter(logging.Filter):
|
||||
# Drop the WARNING "Not Found: ..." records django.request emits for 404s
|
||||
# We have different access logs for that
|
||||
def filter(self, record):
|
||||
return getattr(record, 'status_code', None) != 404
|
||||
|
||||
|
||||
class RequestIdMiddleware:
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
@@ -5,8 +5,8 @@ msgstr ""
|
||||
"Project-Id-Version: 1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-06-28 14:42+0000\n"
|
||||
"PO-Revision-Date: 2026-06-28 15:19+0000\n"
|
||||
"Last-Translator: Raphael Michel <michel@rami.io>\n"
|
||||
"PO-Revision-Date: 2026-06-29 17:00+0000\n"
|
||||
"Last-Translator: CVZ-es <damien.bremont@casadevelazquez.org>\n"
|
||||
"Language-Team: German <https://translate.pretix.eu/projects/pretix/pretix/"
|
||||
"de/>\n"
|
||||
"Language: de\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-06-28 14:42+0000\n"
|
||||
"PO-Revision-Date: 2026-05-29 17:00+0000\n"
|
||||
"PO-Revision-Date: 2026-06-29 17:00+0000\n"
|
||||
"Last-Translator: CVZ-es <damien.bremont@casadevelazquez.org>\n"
|
||||
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/pretix/"
|
||||
"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 2026.5\n"
|
||||
"X-Generator: Weblate 2026.6.1\n"
|
||||
|
||||
#: htmlcov/d_daa1541d0cbf5e2b_dashboards_py.html:670
|
||||
#: pretix/control/templates/pretixcontrol/events/index.html:166
|
||||
@@ -467,10 +467,8 @@ msgid "Medium connected to other event"
|
||||
msgstr "Medio conectado a otro evento"
|
||||
|
||||
#: pretix/api/views/checkin.py:814
|
||||
#, fuzzy
|
||||
#| msgid "You cannot change this order."
|
||||
msgid "You cannot exchange a medium for a medium."
|
||||
msgstr "No puedes cambiar este pedido."
|
||||
msgstr "No se puede cambiar un medio por otro."
|
||||
|
||||
#: pretix/api/views/oauth.py:107 pretix/control/logdisplay.py:777
|
||||
#, python-brace-format
|
||||
@@ -4730,22 +4728,16 @@ msgid "Check-in annulled"
|
||||
msgstr "Check-in anulado"
|
||||
|
||||
#: pretix/base/models/checkin.py:372
|
||||
#, fuzzy
|
||||
#| msgid "Ticket already used"
|
||||
msgid "Ticket already exchanged"
|
||||
msgstr "Esta entrada ya fue utilizada"
|
||||
msgstr "Entrada ya canjeada"
|
||||
|
||||
#: pretix/base/models/checkin.py:373
|
||||
#, fuzzy
|
||||
#| msgid "Reusable media"
|
||||
msgid "Reusable medium invalid"
|
||||
msgstr "Medios reutilizables"
|
||||
msgstr "Soporte reutilizable no válido"
|
||||
|
||||
#: pretix/base/models/checkin.py:374
|
||||
#, fuzzy
|
||||
#| msgid "Reusable media type"
|
||||
msgid "Reusable medium already exists"
|
||||
msgstr "Tipo de medio reusable"
|
||||
msgstr "El soporte reutilizable ya existe"
|
||||
|
||||
#: pretix/base/models/customers.py:63
|
||||
msgid "Provider name"
|
||||
@@ -5330,11 +5322,8 @@ msgstr ""
|
||||
"ambas cosas."
|
||||
|
||||
#: pretix/base/models/event.py:1884
|
||||
#, fuzzy
|
||||
#| msgid "The bundled item must belong to the same event as the item."
|
||||
msgid "Property and event must belong to the same organizer."
|
||||
msgstr ""
|
||||
"La agrupación de artículos debe pertenecer al mismo evento que el artículo."
|
||||
msgstr "La propiedad y el evento deben pertenecer al mismo organizador."
|
||||
|
||||
#: pretix/base/models/event.py:1928 pretix/base/models/organizer.py:627
|
||||
msgid "Link text"
|
||||
@@ -5586,42 +5575,41 @@ msgid "Show product with info on why it’s unavailable"
|
||||
msgstr "Mostrar el producto con la razón que no está disponible"
|
||||
|
||||
#: pretix/base/models/items.py:458 pretix/base/models/items.py:786
|
||||
#, fuzzy
|
||||
#| msgid "Don't use re-usable media, use regular one-off tickets"
|
||||
msgid "Don't use reusable media, use regular one-off tickets"
|
||||
msgstr "No usar medios reutilizables, usar entradas de un solo uso"
|
||||
msgstr ""
|
||||
"No utilizar soportes reutilizables, sino billetes normales de un solo uso"
|
||||
|
||||
#: pretix/base/models/items.py:459
|
||||
msgid "Require a previously unknown medium to be newly added"
|
||||
msgstr "Exigir que un medio desconocido anteriormente se adicione de nuevo"
|
||||
|
||||
#: pretix/base/models/items.py:460
|
||||
#, fuzzy
|
||||
#| msgid "Require an existing medium to be re-used"
|
||||
msgid "Require an existing medium to be reused, replacing any previous tickets"
|
||||
msgstr "Exigir que se reuse un medio ya existente"
|
||||
msgstr ""
|
||||
"Necesita que se reutilice un soporte ya existente, sustituyendo cualquier "
|
||||
"billete anterior"
|
||||
|
||||
#: pretix/base/models/items.py:461
|
||||
#, fuzzy
|
||||
#| msgid "Require either an existing or a new medium to be used"
|
||||
msgid ""
|
||||
"Require either an existing or a new medium to be used, replacing any "
|
||||
"previous tickets"
|
||||
msgstr "Exigir que se use un medio existente o uno nuevo"
|
||||
msgstr ""
|
||||
"Se debe utilizar un soporte ya existente o uno nuevo, sustituyendo cualquier "
|
||||
"billete anterior"
|
||||
|
||||
#: pretix/base/models/items.py:462
|
||||
#, fuzzy
|
||||
#| msgid "Require an existing medium to be re-used"
|
||||
msgid "Require an existing medium to be reused, adding to any previous tickets"
|
||||
msgstr "Exigir que se reuse un medio ya existente"
|
||||
msgstr ""
|
||||
"Exigir que se reutilice un soporte ya existente, añadiendo esta información "
|
||||
"a cualquier entrada anterior"
|
||||
|
||||
#: pretix/base/models/items.py:464
|
||||
#, fuzzy
|
||||
#| msgid "Require either an existing or a new medium to be used"
|
||||
msgid ""
|
||||
"Require either an existing or a new medium to be used, adding to any "
|
||||
"previous tickets"
|
||||
msgstr "Exigir que se use un medio existente o uno nuevo"
|
||||
msgstr ""
|
||||
"Es necesario utilizar un medio ya existente o uno nuevo, que se sumará a los "
|
||||
"billetes anteriores"
|
||||
|
||||
#: pretix/base/models/items.py:480 pretix/base/models/items.py:1468
|
||||
msgid "Category"
|
||||
@@ -5981,14 +5969,6 @@ msgid "Reusable media policy"
|
||||
msgstr "Condiciones de utilización de medios"
|
||||
|
||||
#: pretix/base/models/items.py:777
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "If this product should be stored on a re-usable physical medium, you can "
|
||||
#| "attach a physical media policy. This is not required for regular tickets, "
|
||||
#| "which just use a one-time barcode, but only for products like renewable "
|
||||
#| "season tickets or re-chargeable gift card wristbands. This is an advanced "
|
||||
#| "feature that also requires specific configuration of ticketing and "
|
||||
#| "printing settings."
|
||||
msgid ""
|
||||
"If this product should be stored on a reusable physical medium, you can "
|
||||
"attach a physical media policy. This is not required for regular tickets, "
|
||||
@@ -6052,6 +6032,9 @@ msgid ""
|
||||
"prior to their usage. Therefore, the selected media policy does not make "
|
||||
"sense for this media type."
|
||||
msgstr ""
|
||||
"El tipo de soporte seleccionado requiere que todos los soportes se registren "
|
||||
"en el sistema antes de su uso. Por lo tanto, la política de soportes "
|
||||
"seleccionada no es aplicable a este tipo de soporte."
|
||||
|
||||
#: pretix/base/models/items.py:1009
|
||||
msgid ""
|
||||
@@ -6626,18 +6609,16 @@ msgstr "rebotado"
|
||||
#: pretix/base/models/media.py:77
|
||||
msgctxt "reusable_medium"
|
||||
msgid "Claim token"
|
||||
msgstr ""
|
||||
msgstr "Canjear el token"
|
||||
|
||||
#: pretix/base/models/media.py:82
|
||||
msgctxt "reusable_medium"
|
||||
msgid "Label"
|
||||
msgstr ""
|
||||
msgstr "Designación"
|
||||
|
||||
#: pretix/base/models/media.py:105
|
||||
#, fuzzy
|
||||
#| msgid "Linked ticket"
|
||||
msgid "Linked tickets"
|
||||
msgstr "Entrada vinculada"
|
||||
msgstr "Entradas vinculadas"
|
||||
|
||||
#: pretix/base/models/media.py:107
|
||||
msgid ""
|
||||
@@ -6645,6 +6626,9 @@ msgid ""
|
||||
"validity. If multiple tickets are valid at once, this will lead to failed "
|
||||
"check-ins."
|
||||
msgstr ""
|
||||
"Si enlaza más de un billete, asegúrese de que no haya solapamiento en la "
|
||||
"validez. Si varios billetes son válidos al mismo tiempo, esto provocará que "
|
||||
"no se puedan realizar el check-in."
|
||||
|
||||
#: pretix/base/models/memberships.py:44
|
||||
#: pretix/presale/templates/pretixpresale/organizers/customer_memberships.html:28
|
||||
@@ -8390,14 +8374,10 @@ msgid "Atlantis"
|
||||
msgstr "Atlántida"
|
||||
|
||||
#: pretix/base/pdf.py:376
|
||||
#, fuzzy
|
||||
#| msgid "Invoice recipient email"
|
||||
msgid "Invoice custom recipient field"
|
||||
msgstr "Correo electrónico del destinatario de la factura"
|
||||
msgstr "Campo personalizado de destinatario en la factura"
|
||||
|
||||
#: pretix/base/pdf.py:377
|
||||
#, fuzzy
|
||||
#| msgid "Custom recipient field label"
|
||||
msgid "Custom recipient field"
|
||||
msgstr "Campo de destinatario personalizado"
|
||||
|
||||
@@ -9415,13 +9395,15 @@ msgstr "Necesitas responder preguntas para terminar el check-in."
|
||||
|
||||
#: pretix/base/services/checkin.py:1121
|
||||
msgid "Ticket needs to be exchanged to a suitable medium."
|
||||
msgstr ""
|
||||
msgstr "El billete debe canjearse por un soporte adecuado."
|
||||
|
||||
#: pretix/base/services/checkin.py:1128
|
||||
msgid ""
|
||||
"This ticket has already been exchanged for a reusable medium that now needs "
|
||||
"to be used instead."
|
||||
msgstr ""
|
||||
"Esta entrada ya se ha canjeado por un soporte reutilizable que ahora hay que "
|
||||
"utilizar en su lugar."
|
||||
|
||||
#: pretix/base/services/checkin.py:1180
|
||||
msgid "This ticket has already been redeemed."
|
||||
@@ -9587,64 +9569,46 @@ msgstr ""
|
||||
"{event}."
|
||||
|
||||
#: pretix/base/services/media.py:93 pretix/base/services/media.py:95
|
||||
#, fuzzy
|
||||
#| msgid "Invalid input type."
|
||||
msgid "Invalid medium type."
|
||||
msgstr "Tipo de entrada no válido."
|
||||
msgstr "Tipo de soporte no válido."
|
||||
|
||||
#: pretix/base/services/media.py:100 pretix/base/services/media.py:102
|
||||
#, fuzzy
|
||||
#| msgid "The selected media type is not enabled in your organizer settings."
|
||||
msgid "Medium type is not enabled for organizer."
|
||||
msgstr ""
|
||||
"El tipo de medio seleccionado no está activo en tus ajustes de organizador/a/"
|
||||
"e."
|
||||
msgstr "Este tipo de medio no está habilitado para el organizador."
|
||||
|
||||
#: pretix/base/services/media.py:107 pretix/base/services/media.py:109
|
||||
msgid "Incorrect medium type for product."
|
||||
msgstr ""
|
||||
msgstr "El tipo de soporte no es el adecuado para este producto."
|
||||
|
||||
#: pretix/base/services/media.py:114 pretix/base/services/media.py:116
|
||||
#, fuzzy
|
||||
#| msgid "This ticket has already been redeemed."
|
||||
msgid "Ticket is already exchanged for reusable medium."
|
||||
msgstr "Esta entrada ya ha sido canjeada."
|
||||
msgstr "La entrada ya se ha canjeado por un soporte reutilizable."
|
||||
|
||||
#: pretix/base/services/media.py:133 pretix/base/services/media.py:135
|
||||
#, fuzzy
|
||||
#| msgid "Reusable Medium ID"
|
||||
msgid "Reusable medium not found."
|
||||
msgstr "ID mediana reutilizable"
|
||||
msgstr "No se ha encontrado el soporte reutilizable."
|
||||
|
||||
#: pretix/base/services/media.py:140 pretix/base/services/media.py:142
|
||||
#: pretix/base/services/media.py:168 pretix/base/services/media.py:170
|
||||
#, fuzzy
|
||||
#| msgid "The reusable medium has been created."
|
||||
msgid "Reusable medium is inactive or expired."
|
||||
msgstr "Se ha creado el medio reutilizable."
|
||||
msgstr "El medio reutilizable está inactivo o caducado."
|
||||
|
||||
#: pretix/base/services/media.py:155 pretix/base/services/media.py:162
|
||||
#: pretix/base/services/media.py:176
|
||||
#, fuzzy
|
||||
#| msgid "The reusable medium has been created."
|
||||
msgid "Reusable medium not found and could not be created."
|
||||
msgstr "Se ha creado el medio reutilizable."
|
||||
msgstr "No se ha encontrado el soporte reutilizable y no se ha podido crear."
|
||||
|
||||
#: pretix/base/services/media.py:183
|
||||
#, fuzzy
|
||||
#| msgid "Reusable media type"
|
||||
msgid "Reusable medium already exists."
|
||||
msgstr "Tipo de medio reusable"
|
||||
msgstr "Ya existe un soporte reutilizable."
|
||||
|
||||
#: pretix/base/services/media.py:189
|
||||
#, fuzzy
|
||||
#| msgid "The reusable medium has been created."
|
||||
msgid "Reusable medium could not be created."
|
||||
msgstr "Se ha creado el medio reutilizable."
|
||||
msgstr "No se ha podido crear el soporte reutilizable."
|
||||
|
||||
#: pretix/base/services/media.py:195 pretix/base/services/media.py:197
|
||||
msgid "Product does not support medium exchange."
|
||||
msgstr ""
|
||||
msgstr "Este producto no admite el cambio de medio."
|
||||
|
||||
#: pretix/base/services/memberships.py:108
|
||||
#, python-brace-format
|
||||
@@ -10368,17 +10332,10 @@ msgstr ""
|
||||
"inició sesión durante la compra."
|
||||
|
||||
#: pretix/base/settings.py:214
|
||||
#, fuzzy
|
||||
#| msgid "Activate re-usable media"
|
||||
msgid "Activate reusable media"
|
||||
msgstr "Activar medios reutilizables"
|
||||
|
||||
#: pretix/base/settings.py:215
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "The re-usable media feature allows you to connect tickets and gift cards "
|
||||
#| "with physical media such as wristbands or chip cards that may be re-used "
|
||||
#| "for different tickets or gift cards later."
|
||||
msgid ""
|
||||
"The reusable media feature allows you to connect tickets and gift cards with "
|
||||
"physical media such as wristbands or chip cards that may be reused for "
|
||||
@@ -10390,7 +10347,7 @@ msgstr ""
|
||||
|
||||
#: pretix/base/settings.py:226
|
||||
msgid "Enforce the usage of issued reusable media for check-in"
|
||||
msgstr ""
|
||||
msgstr "Exigir el uso de los soportes reutilizables para el check-in"
|
||||
|
||||
#: pretix/base/settings.py:227
|
||||
msgid ""
|
||||
@@ -10398,6 +10355,10 @@ msgid ""
|
||||
"medium has been created and linked to a ticket. Keeping this option turned "
|
||||
"off will treat the reusable medium and ticket as equals."
|
||||
msgstr ""
|
||||
"Si se activa esta opción, ya no se aceptarán los códigos de barras de los "
|
||||
"billetes cuando se haya creado un soporte reutilizable y se haya vinculado a "
|
||||
"un billete. Si se mantiene desactivada esta opción, el soporte reutilizable "
|
||||
"y el billete se tratarán como iguales."
|
||||
|
||||
#: pretix/base/settings.py:254
|
||||
msgid "Length of barcodes"
|
||||
@@ -18548,16 +18509,12 @@ msgid "The reusable medium has been changed."
|
||||
msgstr "El medio reutilizable ha sido modificado."
|
||||
|
||||
#: pretix/control/logdisplay.py:746
|
||||
#, fuzzy
|
||||
#| msgid "The new member has been added to the team."
|
||||
msgid "A new ticket has been added to the medium."
|
||||
msgstr "El nuevo miembro ha sido añadido al equipo."
|
||||
msgstr "Se ha añadido un nuevo billete al medio."
|
||||
|
||||
#: pretix/control/logdisplay.py:747
|
||||
#, fuzzy
|
||||
#| msgid "{user} has been removed from the team."
|
||||
msgid "A ticket has been removed from the medium."
|
||||
msgstr "{user} ha sido removido del equipo."
|
||||
msgstr "Se ha eliminado un billete del medio."
|
||||
|
||||
#: pretix/control/logdisplay.py:748
|
||||
msgid "The medium has been connected to a new ticket."
|
||||
@@ -18569,6 +18526,8 @@ msgid ""
|
||||
"The ticket #{positionid} was exchanged for reusable medium "
|
||||
"{medium_identifier}."
|
||||
msgstr ""
|
||||
"El billete n.º {positionid} se ha canjeado por un medio reutilizable "
|
||||
"{medium_identifier}."
|
||||
|
||||
#: pretix/control/logdisplay.py:750
|
||||
msgid "The medium has been connected to a new gift card."
|
||||
@@ -20483,10 +20442,8 @@ msgstr ""
|
||||
"check-in:"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/checkin/simulator.html:85
|
||||
#, fuzzy
|
||||
#| msgid "Special attention required"
|
||||
msgid "Media exchange required"
|
||||
msgstr "Atención especial requerida"
|
||||
msgstr "Es necesario intercambiar medios"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/checkin/simulator.html:87
|
||||
#, python-format
|
||||
@@ -20494,6 +20451,8 @@ msgid ""
|
||||
"This ticket needs to be exchanged into a <strong>%(media_type)s</strong> "
|
||||
"reusable medium. <strong>%(media_policy)s</strong>."
|
||||
msgstr ""
|
||||
"Esta entrada debe canjearse por un soporte reutilizable <strong>%(media_type)"
|
||||
"s</strong>. <strong>%(media_policy)s</strong>."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/checkin/simulator.html:103
|
||||
msgid "Special attention required"
|
||||
@@ -27038,6 +26997,9 @@ msgid ""
|
||||
"Even if a team has no access to a certain category of data, they might still "
|
||||
"be able to see parts of this data when it is linked to data they can see."
|
||||
msgstr ""
|
||||
"Aunque un equipo no tenga acceso a una determinada categoría de datos, es "
|
||||
"posible que pueda ver parte de esos datos cuando estén vinculados a datos a "
|
||||
"los que sí tiene acceso."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/organizers/team_edit.html:35
|
||||
msgid ""
|
||||
@@ -27045,6 +27007,10 @@ msgid ""
|
||||
"some information about gift cards linked to a customer account, even if they "
|
||||
"generally can't see gift cards directly."
|
||||
msgstr ""
|
||||
"Por ejemplo, una persona con acceso a las cuentas de los clientes podrá ver "
|
||||
"cierta información sobre las tarjetas regalo vinculadas a una cuenta de "
|
||||
"cliente, aunque, por lo general, no pueda ver las tarjetas regalo "
|
||||
"directamente."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/organizers/team_edit.html:59
|
||||
msgid ""
|
||||
@@ -27052,6 +27018,9 @@ msgid ""
|
||||
"information about vouchers used to create an order, even if they generally "
|
||||
"can't see vouchers directly."
|
||||
msgstr ""
|
||||
"Por ejemplo, una persona con acceso a los pedidos podrá ver cierta "
|
||||
"información sobre los vales utilizados para crear un pedido, aunque "
|
||||
"normalmente no pueda ver los vales directamente."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/organizers/team_members.html:21
|
||||
msgid "Member"
|
||||
@@ -27912,30 +27881,22 @@ msgstr ""
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/subevents/detail.html:9
|
||||
#: pretix/control/templates/pretixcontrol/subevents/detail.html:13
|
||||
#, fuzzy, python-format
|
||||
#| msgid "Quota: %(name)s"
|
||||
#, python-format
|
||||
msgctxt "subevent"
|
||||
msgid "Date: %(name)s"
|
||||
msgstr "Cuota: %(name)s"
|
||||
msgstr "Fecha: %(name)s"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/subevents/detail.html:234
|
||||
#, fuzzy
|
||||
#| msgid "Partially paid"
|
||||
msgid "partially canceled"
|
||||
msgstr "Pagado parcialmente"
|
||||
msgstr "cancelado parcialmente"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/subevents/detail.html:282
|
||||
#, fuzzy
|
||||
#| msgctxt "permission_level"
|
||||
#| msgid "View all"
|
||||
msgid "View all"
|
||||
msgstr "Ver todo"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/subevents/detail.html:289
|
||||
#, fuzzy
|
||||
#| msgid "No archived events found."
|
||||
msgid "No orders found."
|
||||
msgstr "No se han encontrado eventos archivados."
|
||||
msgstr "No se han encontrado pedidos."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/subevents/detail.html:302
|
||||
#: pretix/control/templates/pretixcontrol/subevents/edit.html:279
|
||||
@@ -30708,10 +30669,8 @@ msgid "Voucher {}"
|
||||
msgstr "Vale de compra {}"
|
||||
|
||||
#: pretix/control/views/typeahead.py:179 pretix/control/views/typeahead.py:180
|
||||
#, fuzzy
|
||||
#| msgid "Go to event"
|
||||
msgid "No event"
|
||||
msgstr "Ir al evento"
|
||||
msgstr "No hay eventos"
|
||||
|
||||
#: pretix/control/views/user.py:169
|
||||
msgid "The password you entered was invalid, please try again."
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-06-28 15:49+0000\n"
|
||||
"PO-Revision-Date: 2026-03-30 03:00+0000\n"
|
||||
"PO-Revision-Date: 2026-06-29 17:00+0000\n"
|
||||
"Last-Translator: CVZ-es <damien.bremont@casadevelazquez.org>\n"
|
||||
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
"js/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 5.16.2\n"
|
||||
"X-Generator: Weblate 2026.6.1\n"
|
||||
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
|
||||
@@ -442,11 +442,11 @@ msgstr "¡Presione Control+C para copiar!"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules/App.vue:80
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
msgstr "Editar"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules/App.vue:86
|
||||
msgid "Visualize"
|
||||
msgstr ""
|
||||
msgstr "Visualizar"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules/App.vue:96
|
||||
msgid ""
|
||||
@@ -454,10 +454,13 @@ msgid ""
|
||||
"or variations are not contained in any of your rule parts so people with "
|
||||
"these tickets will not get in:"
|
||||
msgstr ""
|
||||
"Su regla siempre filtra por producto o variante, pero los siguientes "
|
||||
"productos o variantes no figuran en ninguna de las partes de su regla, por "
|
||||
"lo que las personas con estos tickets no podrán acceder:"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules/App.vue:99
|
||||
msgid "Please double-check if this was intentional."
|
||||
msgstr ""
|
||||
msgstr "Por favor, comprueba bien si esto ha sido a propósito."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:4
|
||||
msgid "All of the conditions below (AND)"
|
||||
|
||||
@@ -4,10 +4,10 @@ msgstr ""
|
||||
"Project-Id-Version: 1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-06-28 14:42+0000\n"
|
||||
"PO-Revision-Date: 2026-06-08 17:00+0000\n"
|
||||
"Last-Translator: Sébastien BRUNEAU <s.bruneau@beauvaisis.fr>\n"
|
||||
"Language-Team: French <https://translate.pretix.eu/projects/pretix/pretix/fr/"
|
||||
">\n"
|
||||
"PO-Revision-Date: 2026-06-29 17:00+0000\n"
|
||||
"Last-Translator: CVZ-es <damien.bremont@casadevelazquez.org>\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"
|
||||
@@ -466,10 +466,8 @@ msgid "Medium connected to other event"
|
||||
msgstr "Média connecté à un autre événement"
|
||||
|
||||
#: pretix/api/views/checkin.py:814
|
||||
#, fuzzy
|
||||
#| msgid "You cannot change this order."
|
||||
msgid "You cannot exchange a medium for a medium."
|
||||
msgstr "Vous ne pouvez pas modifier cette commande."
|
||||
msgstr "Il n'est pas possible d'échanger un support contre un autre support."
|
||||
|
||||
#: pretix/api/views/oauth.py:107 pretix/control/logdisplay.py:777
|
||||
#, python-brace-format
|
||||
@@ -4736,22 +4734,16 @@ msgid "Check-in annulled"
|
||||
msgstr "Enregistrement annulé"
|
||||
|
||||
#: pretix/base/models/checkin.py:372
|
||||
#, fuzzy
|
||||
#| msgid "Ticket already used"
|
||||
msgid "Ticket already exchanged"
|
||||
msgstr "Billet déjà utilisé"
|
||||
msgstr "Billet déjà échangé"
|
||||
|
||||
#: pretix/base/models/checkin.py:373
|
||||
#, fuzzy
|
||||
#| msgid "Reusable media"
|
||||
msgid "Reusable medium invalid"
|
||||
msgstr "Support réutilisable"
|
||||
msgstr "Support réutilisable non valide"
|
||||
|
||||
#: pretix/base/models/checkin.py:374
|
||||
#, fuzzy
|
||||
#| msgid "Reusable media type"
|
||||
msgid "Reusable medium already exists"
|
||||
msgstr "Type de support réutilisable"
|
||||
msgstr "Il existe déjà un support réutilisable"
|
||||
|
||||
#: pretix/base/models/customers.py:63
|
||||
msgid "Provider name"
|
||||
@@ -5343,10 +5335,8 @@ msgstr ""
|
||||
"deux."
|
||||
|
||||
#: pretix/base/models/event.py:1884
|
||||
#, fuzzy
|
||||
#| msgid "The bundled item must belong to the same event as the item."
|
||||
msgid "Property and event must belong to the same organizer."
|
||||
msgstr "L’élément groupé doit appartenir au même événement que l’élément."
|
||||
msgstr "La propriété et l'événement doivent appartenir au même organisateur."
|
||||
|
||||
#: pretix/base/models/event.py:1928 pretix/base/models/organizer.py:627
|
||||
msgid "Link text"
|
||||
@@ -5601,8 +5591,6 @@ msgstr ""
|
||||
"indisponibilité"
|
||||
|
||||
#: pretix/base/models/items.py:458 pretix/base/models/items.py:786
|
||||
#, fuzzy
|
||||
#| msgid "Don't use re-usable media, use regular one-off tickets"
|
||||
msgid "Don't use reusable media, use regular one-off tickets"
|
||||
msgstr ""
|
||||
"N'utilisez pas de supports réutilisables, mais plutôt des tickets uniques "
|
||||
@@ -5613,32 +5601,30 @@ msgid "Require a previously unknown medium to be newly added"
|
||||
msgstr "Exiger l'ajout d'un support inconnu jusqu'alors"
|
||||
|
||||
#: pretix/base/models/items.py:460
|
||||
#, fuzzy
|
||||
#| msgid "Require an existing medium to be re-used"
|
||||
msgid "Require an existing medium to be reused, replacing any previous tickets"
|
||||
msgstr "Exiger la réutilisation d'un support existant"
|
||||
|
||||
#: pretix/base/models/items.py:461
|
||||
#, fuzzy
|
||||
#| msgid "Require either an existing or a new medium to be used"
|
||||
msgid ""
|
||||
"Require either an existing or a new medium to be used, replacing any "
|
||||
"previous tickets"
|
||||
msgstr "Nécessiter l'utilisation d'un support existant ou d'un nouveau support"
|
||||
msgstr ""
|
||||
"Exiger l'utilisation d'un support existant ou d'un nouveau support, en "
|
||||
"remplacement de tout billet antérieur"
|
||||
|
||||
#: pretix/base/models/items.py:462
|
||||
#, fuzzy
|
||||
#| msgid "Require an existing medium to be re-used"
|
||||
msgid "Require an existing medium to be reused, adding to any previous tickets"
|
||||
msgstr "Exiger la réutilisation d'un support existant"
|
||||
msgstr ""
|
||||
"Exiger la réutilisation d'un support existant, en ajoutant cette demande à "
|
||||
"tout ticket précédent"
|
||||
|
||||
#: pretix/base/models/items.py:464
|
||||
#, fuzzy
|
||||
#| msgid "Require either an existing or a new medium to be used"
|
||||
msgid ""
|
||||
"Require either an existing or a new medium to be used, adding to any "
|
||||
"previous tickets"
|
||||
msgstr "Nécessiter l'utilisation d'un support existant ou d'un nouveau support"
|
||||
msgstr ""
|
||||
"Exiger l'utilisation d'un support existant ou d'un nouveau support, en "
|
||||
"complément des tickets précédents"
|
||||
|
||||
#: pretix/base/models/items.py:480 pretix/base/models/items.py:1468
|
||||
msgid "Category"
|
||||
@@ -6001,14 +5987,6 @@ msgid "Reusable media policy"
|
||||
msgstr "Politique relative aux médias réutilisables"
|
||||
|
||||
#: pretix/base/models/items.py:777
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "If this product should be stored on a re-usable physical medium, you can "
|
||||
#| "attach a physical media policy. This is not required for regular tickets, "
|
||||
#| "which just use a one-time barcode, but only for products like renewable "
|
||||
#| "season tickets or re-chargeable gift card wristbands. This is an advanced "
|
||||
#| "feature that also requires specific configuration of ticketing and "
|
||||
#| "printing settings."
|
||||
msgid ""
|
||||
"If this product should be stored on a reusable physical medium, you can "
|
||||
"attach a physical media policy. This is not required for regular tickets, "
|
||||
@@ -6073,6 +6051,10 @@ msgid ""
|
||||
"prior to their usage. Therefore, the selected media policy does not make "
|
||||
"sense for this media type."
|
||||
msgstr ""
|
||||
"Le type de support sélectionné exige que tous les supports soient "
|
||||
"enregistrés dans le système avant leur utilisation. Par conséquent, la "
|
||||
"politique relative aux supports sélectionnée n'est pas applicable à ce type "
|
||||
"de support."
|
||||
|
||||
#: pretix/base/models/items.py:1009
|
||||
msgid ""
|
||||
@@ -6649,18 +6631,16 @@ msgstr "Non distribué"
|
||||
#: pretix/base/models/media.py:77
|
||||
msgctxt "reusable_medium"
|
||||
msgid "Claim token"
|
||||
msgstr ""
|
||||
msgstr "Réclamer un jeton"
|
||||
|
||||
#: pretix/base/models/media.py:82
|
||||
msgctxt "reusable_medium"
|
||||
msgid "Label"
|
||||
msgstr ""
|
||||
msgstr "Descriptif"
|
||||
|
||||
#: pretix/base/models/media.py:105
|
||||
#, fuzzy
|
||||
#| msgid "Linked ticket"
|
||||
msgid "Linked tickets"
|
||||
msgstr "Billet lié"
|
||||
msgstr "Billets liés"
|
||||
|
||||
#: pretix/base/models/media.py:107
|
||||
msgid ""
|
||||
@@ -6668,6 +6648,9 @@ msgid ""
|
||||
"validity. If multiple tickets are valid at once, this will lead to failed "
|
||||
"check-ins."
|
||||
msgstr ""
|
||||
"Si vous associez plusieurs billets, assurez-vous qu'il n'y ait pas de "
|
||||
"chevauchement entre leurs périodes de validité. Si plusieurs billets sont "
|
||||
"valables en même temps, cela entraînera l'échec de l'enregistrement."
|
||||
|
||||
#: pretix/base/models/memberships.py:44
|
||||
#: pretix/presale/templates/pretixpresale/organizers/customer_memberships.html:28
|
||||
@@ -8437,16 +8420,12 @@ msgid "Atlantis"
|
||||
msgstr "Atlantide"
|
||||
|
||||
#: pretix/base/pdf.py:376
|
||||
#, fuzzy
|
||||
#| msgid "Invoice recipient email"
|
||||
msgid "Invoice custom recipient field"
|
||||
msgstr "E-mail du destinataire de la facture"
|
||||
msgstr "Champ personnalisé destinataire de la facture"
|
||||
|
||||
#: pretix/base/pdf.py:377
|
||||
#, fuzzy
|
||||
#| msgid "Custom recipient field label"
|
||||
msgid "Custom recipient field"
|
||||
msgstr "Libellé personnalisé du champ destinataire"
|
||||
msgstr "Champ de destinataire personnalisé"
|
||||
|
||||
#: pretix/base/pdf.py:381
|
||||
msgid "List of Add-Ons"
|
||||
@@ -9471,13 +9450,15 @@ msgstr ""
|
||||
|
||||
#: pretix/base/services/checkin.py:1121
|
||||
msgid "Ticket needs to be exchanged to a suitable medium."
|
||||
msgstr ""
|
||||
msgstr "Le billet doit être échangé contre un support adapté."
|
||||
|
||||
#: pretix/base/services/checkin.py:1128
|
||||
msgid ""
|
||||
"This ticket has already been exchanged for a reusable medium that now needs "
|
||||
"to be used instead."
|
||||
msgstr ""
|
||||
"Ce billet a déjà été échangé contre un support réutilisable qui doit "
|
||||
"désormais être utilisé à sa place."
|
||||
|
||||
#: pretix/base/services/checkin.py:1180
|
||||
msgid "This ticket has already been redeemed."
|
||||
@@ -9643,64 +9624,46 @@ msgstr ""
|
||||
"Vous recevez cet e-mail parce que vous avez passé une commande pour {event}."
|
||||
|
||||
#: pretix/base/services/media.py:93 pretix/base/services/media.py:95
|
||||
#, fuzzy
|
||||
#| msgid "Invalid input type."
|
||||
msgid "Invalid medium type."
|
||||
msgstr "Type d’entrée non valide."
|
||||
msgstr "Type de support non valide."
|
||||
|
||||
#: pretix/base/services/media.py:100 pretix/base/services/media.py:102
|
||||
#, fuzzy
|
||||
#| msgid "The selected media type is not enabled in your organizer settings."
|
||||
msgid "Medium type is not enabled for organizer."
|
||||
msgstr ""
|
||||
"Le type de média sélectionné n’est pas activé dans les paramètres de votre "
|
||||
"organisateur."
|
||||
msgstr "Ce type de média n’est pas activé par l'organisateur."
|
||||
|
||||
#: pretix/base/services/media.py:107 pretix/base/services/media.py:109
|
||||
msgid "Incorrect medium type for product."
|
||||
msgstr ""
|
||||
msgstr "Type de support incorrect pour ce produit."
|
||||
|
||||
#: pretix/base/services/media.py:114 pretix/base/services/media.py:116
|
||||
#, fuzzy
|
||||
#| msgid "This ticket has already been redeemed."
|
||||
msgid "Ticket is already exchanged for reusable medium."
|
||||
msgstr "Ce billet a déjà été échangé."
|
||||
msgstr "Le billet a déjà été échangé contre un support réutilisable."
|
||||
|
||||
#: pretix/base/services/media.py:133 pretix/base/services/media.py:135
|
||||
#, fuzzy
|
||||
#| msgid "Reusable Medium ID"
|
||||
msgid "Reusable medium not found."
|
||||
msgstr "Identification de support réutilisable"
|
||||
msgstr "Support réutilisable introuvable."
|
||||
|
||||
#: pretix/base/services/media.py:140 pretix/base/services/media.py:142
|
||||
#: pretix/base/services/media.py:168 pretix/base/services/media.py:170
|
||||
#, fuzzy
|
||||
#| msgid "The reusable medium has been created."
|
||||
msgid "Reusable medium is inactive or expired."
|
||||
msgstr "Le support réutilisable a été créé."
|
||||
msgstr "Le support réutilisable est inactif ou a expiré."
|
||||
|
||||
#: pretix/base/services/media.py:155 pretix/base/services/media.py:162
|
||||
#: pretix/base/services/media.py:176
|
||||
#, fuzzy
|
||||
#| msgid "The reusable medium has been created."
|
||||
msgid "Reusable medium not found and could not be created."
|
||||
msgstr "Le support réutilisable a été créé."
|
||||
msgstr "Support réutilisable introuvable et impossible à créer."
|
||||
|
||||
#: pretix/base/services/media.py:183
|
||||
#, fuzzy
|
||||
#| msgid "Reusable media type"
|
||||
msgid "Reusable medium already exists."
|
||||
msgstr "Type de support réutilisable"
|
||||
msgstr "Le type de support réutilisable existe déjà."
|
||||
|
||||
#: pretix/base/services/media.py:189
|
||||
#, fuzzy
|
||||
#| msgid "The reusable medium has been created."
|
||||
msgid "Reusable medium could not be created."
|
||||
msgstr "Le support réutilisable a été créé."
|
||||
msgstr "Impossible de créer le support réutilisable."
|
||||
|
||||
#: pretix/base/services/media.py:195 pretix/base/services/media.py:197
|
||||
msgid "Product does not support medium exchange."
|
||||
msgstr ""
|
||||
msgstr "Ce produit ne permet pas de changer de support."
|
||||
|
||||
#: pretix/base/services/memberships.py:108
|
||||
#, python-brace-format
|
||||
@@ -10426,17 +10389,10 @@ msgstr ""
|
||||
"l’achat."
|
||||
|
||||
#: pretix/base/settings.py:214
|
||||
#, fuzzy
|
||||
#| msgid "Activate re-usable media"
|
||||
msgid "Activate reusable media"
|
||||
msgstr "Activer les supports réutilisables"
|
||||
|
||||
#: pretix/base/settings.py:215
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "The re-usable media feature allows you to connect tickets and gift cards "
|
||||
#| "with physical media such as wristbands or chip cards that may be re-used "
|
||||
#| "for different tickets or gift cards later."
|
||||
msgid ""
|
||||
"The reusable media feature allows you to connect tickets and gift cards with "
|
||||
"physical media such as wristbands or chip cards that may be reused for "
|
||||
@@ -10450,6 +10406,8 @@ msgstr ""
|
||||
#: pretix/base/settings.py:226
|
||||
msgid "Enforce the usage of issued reusable media for check-in"
|
||||
msgstr ""
|
||||
"Imposer l'utilisation de supports réutilisables fournis lors de "
|
||||
"l'enregistrement"
|
||||
|
||||
#: pretix/base/settings.py:227
|
||||
msgid ""
|
||||
@@ -10457,6 +10415,10 @@ msgid ""
|
||||
"medium has been created and linked to a ticket. Keeping this option turned "
|
||||
"off will treat the reusable medium and ticket as equals."
|
||||
msgstr ""
|
||||
"Si cette option est activée, le code-barres d'un billet ne sera plus accepté "
|
||||
"dès lors qu'un support réutilisable a été créé et associé à ce billet. Si "
|
||||
"cette option reste désactivée, le support réutilisable et le billet seront "
|
||||
"considérés comme équivalents."
|
||||
|
||||
#: pretix/base/settings.py:254
|
||||
msgid "Length of barcodes"
|
||||
@@ -18691,16 +18653,12 @@ msgid "The reusable medium has been changed."
|
||||
msgstr "Le support réutilisable a été changé."
|
||||
|
||||
#: pretix/control/logdisplay.py:746
|
||||
#, fuzzy
|
||||
#| msgid "The new member has been added to the team."
|
||||
msgid "A new ticket has been added to the medium."
|
||||
msgstr "Le nouveau membre a été ajouté à l'équipe."
|
||||
msgstr "Un nouveau billet a été ajouté sur le support."
|
||||
|
||||
#: pretix/control/logdisplay.py:747
|
||||
#, fuzzy
|
||||
#| msgid "{user} has been removed from the team."
|
||||
msgid "A ticket has been removed from the medium."
|
||||
msgstr "{user} a été retiré de l'équipe."
|
||||
msgstr "Un billet a été retiré du support."
|
||||
|
||||
#: pretix/control/logdisplay.py:748
|
||||
msgid "The medium has been connected to a new ticket."
|
||||
@@ -18712,6 +18670,8 @@ msgid ""
|
||||
"The ticket #{positionid} was exchanged for reusable medium "
|
||||
"{medium_identifier}."
|
||||
msgstr ""
|
||||
"Le ticket n° {positionid} a été échangé contre un support réutilisable "
|
||||
"{medium_identifier}."
|
||||
|
||||
#: pretix/control/logdisplay.py:750
|
||||
msgid "The medium has been connected to a new gift card."
|
||||
@@ -20627,10 +20587,8 @@ msgstr ""
|
||||
"l’enregistrement :"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/checkin/simulator.html:85
|
||||
#, fuzzy
|
||||
#| msgid "Special attention required"
|
||||
msgid "Media exchange required"
|
||||
msgstr "Une attention particulière est requise"
|
||||
msgstr "Échange de supports requis"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/checkin/simulator.html:87
|
||||
#, python-format
|
||||
@@ -20638,6 +20596,8 @@ msgid ""
|
||||
"This ticket needs to be exchanged into a <strong>%(media_type)s</strong> "
|
||||
"reusable medium. <strong>%(media_policy)s</strong>."
|
||||
msgstr ""
|
||||
"Ce billet doit être échangé contre un support réutilisable <strong>%"
|
||||
"(media_type)s</strong>. <strong>%(media_policy)s</strong>."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/checkin/simulator.html:103
|
||||
msgid "Special attention required"
|
||||
@@ -27232,6 +27192,9 @@ msgid ""
|
||||
"Even if a team has no access to a certain category of data, they might still "
|
||||
"be able to see parts of this data when it is linked to data they can see."
|
||||
msgstr ""
|
||||
"Même si une équipe n'a pas accès à une certaine catégorie de données, elle "
|
||||
"peut néanmoins être en mesure de consulter certaines parties de ces données "
|
||||
"lorsque celles-ci sont liées à des données auxquelles elle a accès."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/organizers/team_edit.html:35
|
||||
msgid ""
|
||||
@@ -27239,6 +27202,10 @@ msgid ""
|
||||
"some information about gift cards linked to a customer account, even if they "
|
||||
"generally can't see gift cards directly."
|
||||
msgstr ""
|
||||
"Par exemple, une personne ayant accès aux comptes clients pourra consulter "
|
||||
"certaines informations concernant les cartes cadeaux associées à un compte "
|
||||
"client, même si, en règle générale, elle ne peut pas voir directement ces "
|
||||
"cartes cadeaux."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/organizers/team_edit.html:59
|
||||
msgid ""
|
||||
@@ -27246,6 +27213,9 @@ msgid ""
|
||||
"information about vouchers used to create an order, even if they generally "
|
||||
"can't see vouchers directly."
|
||||
msgstr ""
|
||||
"Par exemple, une personne ayant accès aux commandes pourra consulter "
|
||||
"certaines informations concernant les bons utilisés pour créer une commande, "
|
||||
"même si, en règle générale, elle ne peut pas consulter directement ces bons."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/organizers/team_members.html:21
|
||||
msgid "Member"
|
||||
@@ -28117,30 +28087,22 @@ msgstr ""
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/subevents/detail.html:9
|
||||
#: pretix/control/templates/pretixcontrol/subevents/detail.html:13
|
||||
#, fuzzy, python-format
|
||||
#| msgid "Quota: %(name)s"
|
||||
#, python-format
|
||||
msgctxt "subevent"
|
||||
msgid "Date: %(name)s"
|
||||
msgstr "Quota : %(name)s"
|
||||
msgstr "Date : %(name)s"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/subevents/detail.html:234
|
||||
#, fuzzy
|
||||
#| msgid "Partially paid"
|
||||
msgid "partially canceled"
|
||||
msgstr "Partiellement payé"
|
||||
msgstr "partiellement annulé"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/subevents/detail.html:282
|
||||
#, fuzzy
|
||||
#| msgctxt "permission_level"
|
||||
#| msgid "View all"
|
||||
msgid "View all"
|
||||
msgstr "Tout afficher"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/subevents/detail.html:289
|
||||
#, fuzzy
|
||||
#| msgid "No archived events found."
|
||||
msgid "No orders found."
|
||||
msgstr "Aucun événement archivé trouvé."
|
||||
msgstr "Aucune commande trouvée."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/subevents/detail.html:302
|
||||
#: pretix/control/templates/pretixcontrol/subevents/edit.html:279
|
||||
@@ -30938,10 +30900,8 @@ msgid "Voucher {}"
|
||||
msgstr "Bon {}"
|
||||
|
||||
#: pretix/control/views/typeahead.py:179 pretix/control/views/typeahead.py:180
|
||||
#, fuzzy
|
||||
#| msgid "Go to event"
|
||||
msgid "No event"
|
||||
msgstr "Aller à l'événement"
|
||||
msgstr "Aucun événement"
|
||||
|
||||
#: pretix/control/views/user.py:169
|
||||
msgid "The password you entered was invalid, please try again."
|
||||
|
||||
@@ -7,7 +7,7 @@ msgstr ""
|
||||
"Project-Id-Version: French\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-06-28 15:49+0000\n"
|
||||
"PO-Revision-Date: 2026-03-18 12:23+0000\n"
|
||||
"PO-Revision-Date: 2026-06-29 17:00+0000\n"
|
||||
"Last-Translator: CVZ-es <damien.bremont@casadevelazquez.org>\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 5.16.2\n"
|
||||
"X-Generator: Weblate 2026.6.1\n"
|
||||
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
|
||||
@@ -443,11 +443,11 @@ msgstr "Appuyez sur Ctrl-C pour copier !"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules/App.vue:80
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
msgstr "Éditer"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules/App.vue:86
|
||||
msgid "Visualize"
|
||||
msgstr ""
|
||||
msgstr "Visualiser"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules/App.vue:96
|
||||
msgid ""
|
||||
@@ -455,10 +455,14 @@ msgid ""
|
||||
"or variations are not contained in any of your rule parts so people with "
|
||||
"these tickets will not get in:"
|
||||
msgstr ""
|
||||
"Votre règle effectue toujours un filtrage par produit ou variante, mais les "
|
||||
"produits ou variantes suivants ne figurent dans aucune des parties de votre "
|
||||
"règle ; par conséquent, les personnes détenant ces billets ne seront pas "
|
||||
"admises :"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules/App.vue:99
|
||||
msgid "Please double-check if this was intentional."
|
||||
msgstr ""
|
||||
msgstr "Veuillez vérifier si cela était intentionnel."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:4
|
||||
msgid "All of the conditions below (AND)"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-06-28 14:42+0000\n"
|
||||
"PO-Revision-Date: 2026-06-20 17:00+0000\n"
|
||||
"PO-Revision-Date: 2026-06-30 16:52+0000\n"
|
||||
"Last-Translator: Nikita Mitasov <me@ch4og.com>\n"
|
||||
"Language-Team: Russian <https://translate.pretix.eu/projects/pretix/pretix/"
|
||||
"ru/>\n"
|
||||
@@ -35488,10 +35488,8 @@ msgstr "Введите промокод ниже, чтобы купить эт
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_availability.html:10
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_availability.html:14
|
||||
#, fuzzy
|
||||
#| msgid "Quota availabilities"
|
||||
msgid "Not available yet."
|
||||
msgstr "Наличие квот"
|
||||
msgstr "Еще недоступно."
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_availability.html:18
|
||||
msgid "Not available any more."
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
|
||||
import json
|
||||
import logging
|
||||
import urllib.parse
|
||||
from collections import OrderedDict
|
||||
from decimal import Decimal
|
||||
|
||||
@@ -42,7 +41,6 @@ import paypalrestsdk
|
||||
import paypalrestsdk.exceptions
|
||||
from django import forms
|
||||
from django.contrib import messages
|
||||
from django.core import signing
|
||||
from django.http import HttpRequest
|
||||
from django.template.loader import get_template
|
||||
from django.urls import reverse
|
||||
@@ -58,6 +56,7 @@ from pretix.base.forms import SecretKeySettingsField
|
||||
from pretix.base.models import Event, Order, OrderPayment, OrderRefund, Quota
|
||||
from pretix.base.payment import BasePaymentProvider, PaymentException
|
||||
from pretix.base.settings import SettingsSandbox
|
||||
from pretix.base.views.redirect import safelink
|
||||
from pretix.multidomain.urlreverse import eventreverse_absolute
|
||||
from pretix.plugins.paypal.api import Api
|
||||
from pretix.plugins.paypal.models import ReferencedPayPalObject
|
||||
@@ -349,11 +348,7 @@ class Paypal(BasePaymentProvider):
|
||||
for link in payment.links:
|
||||
if link.method == "REDIRECT" and link.rel == "approval_url":
|
||||
if request.session.get('iframe_session', False):
|
||||
signer = signing.Signer(salt='safe-redirect')
|
||||
return (
|
||||
eventreverse_absolute(request.event, 'plugins:paypal:redirect') + '?url=' +
|
||||
urllib.parse.quote(signer.sign(link.href))
|
||||
)
|
||||
return safelink(link.href, framebreak=True)
|
||||
else:
|
||||
return str(link.href)
|
||||
else:
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
{% load compress %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{ settings.PRETIX_INSTANCE_NAME }}</title>
|
||||
{% compress css %}
|
||||
<link rel="stylesheet" type="text/x-scss" href="{% static "pretixbase/scss/cachedfiles.scss" %}"/>
|
||||
{% endcompress %}
|
||||
{% compress js %}
|
||||
<script type="text/javascript" src="{% static "jquery/js/jquery-3.6.4.min.js" %}"></script>
|
||||
{% endcompress %}
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>{% trans "The payment process has started in a new window." %}</h1>
|
||||
|
||||
<p>
|
||||
{% trans "The window to enter your payment data was not opened or was closed?" %}
|
||||
</p>
|
||||
<p>
|
||||
<a href="{{ url }}" target="_blank" class="btn btn-default btn-lg">
|
||||
<span class="fa fa-external-link-square"></span>
|
||||
{% trans "Click here in order to open the window." %}
|
||||
</a>
|
||||
</p>
|
||||
<script>
|
||||
window.open('{{ url|escapejs }}');
|
||||
</script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -21,13 +21,12 @@
|
||||
#
|
||||
from django.urls import include, re_path
|
||||
|
||||
from .views import abort, oauth_disconnect, redirect_view, success
|
||||
from .views import abort, oauth_disconnect, success
|
||||
|
||||
event_patterns = [
|
||||
re_path(r'^paypal/', include([
|
||||
re_path(r'^abort/$', abort, name='abort'),
|
||||
re_path(r'^return/$', success, name='return'),
|
||||
re_path(r'^redirect/$', redirect_view, name='redirect'),
|
||||
|
||||
re_path(r'w/(?P<cart_namespace>[a-zA-Z0-9]{16})/abort/', abort, name='abort'),
|
||||
re_path(r'w/(?P<cart_namespace>[a-zA-Z0-9]{16})/return/', success, name='return'),
|
||||
|
||||
@@ -39,13 +39,10 @@ from decimal import Decimal
|
||||
import paypalrestsdk
|
||||
import paypalrestsdk.exceptions
|
||||
from django.contrib import messages
|
||||
from django.core import signing
|
||||
from django.db.models import Sum
|
||||
from django.http import HttpResponse, HttpResponseBadRequest
|
||||
from django.shortcuts import render
|
||||
from django.http import HttpResponse
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views.decorators.clickjacking import xframe_options_exempt
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.views.decorators.http import require_POST
|
||||
from django_scopes import scopes_disabled
|
||||
@@ -61,21 +58,6 @@ from pretix.plugins.paypal.payment import Paypal
|
||||
logger = logging.getLogger('pretix.plugins.paypal')
|
||||
|
||||
|
||||
@xframe_options_exempt
|
||||
def redirect_view(request, *args, **kwargs):
|
||||
signer = signing.Signer(salt='safe-redirect')
|
||||
try:
|
||||
url = signer.unsign(request.GET.get('url', ''))
|
||||
except signing.BadSignature:
|
||||
return HttpResponseBadRequest('Invalid parameter')
|
||||
|
||||
r = render(request, 'pretixplugins/paypal/redirect.html', {
|
||||
'url': url,
|
||||
})
|
||||
r._csp_ignore = True
|
||||
return r
|
||||
|
||||
|
||||
def success(request, *args, **kwargs):
|
||||
pid = request.GET.get('paymentId')
|
||||
token = request.GET.get('token')
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
{% load compress %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{ settings.PRETIX_INSTANCE_NAME }}</title>
|
||||
{% compress css %}
|
||||
<link rel="stylesheet" type="text/x-scss" href="{% static "pretixbase/scss/cachedfiles.scss" %}"/>
|
||||
{% endcompress %}
|
||||
{% compress js %}
|
||||
<script type="text/javascript" src="{% static "jquery/js/jquery-3.6.4.min.js" %}"></script>
|
||||
{% endcompress %}
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>{% trans "The payment process has started in a new window." %}</h1>
|
||||
|
||||
<p>
|
||||
{% trans "The window to enter your payment data was not opened or was closed?" %}
|
||||
</p>
|
||||
<p>
|
||||
<a href="{{ url }}" target="_blank" class="btn btn-default btn-lg">
|
||||
<span class="fa fa-external-link-square"></span>
|
||||
{% trans "Click here in order to open the window." %}
|
||||
</a>
|
||||
</p>
|
||||
<script>
|
||||
window.open('{{ url|escapejs }}');
|
||||
</script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -22,15 +22,13 @@
|
||||
from django.urls import include, re_path
|
||||
|
||||
from .views import (
|
||||
PayView, XHRView, abort, isu_disconnect, isu_return, redirect_view,
|
||||
success, webhook,
|
||||
PayView, XHRView, abort, isu_disconnect, isu_return, success, webhook,
|
||||
)
|
||||
|
||||
event_patterns = [
|
||||
re_path(r'^paypal2/', include([
|
||||
re_path(r'^abort/$', abort, name='abort'),
|
||||
re_path(r'^return/$', success, name='return'),
|
||||
re_path(r'^redirect/$', redirect_view, name='redirect'),
|
||||
re_path(r'^xhr/$', XHRView.as_view(), name='xhr'),
|
||||
re_path(r'^pay/(?P<order>[^/]+)/(?P<hash>[^/]+)/(?P<payment>[^/]+)/$', PayView.as_view(), name='pay'),
|
||||
re_path(r'^(?P<order>[^/][^w]+)/(?P<secret>[A-Za-z0-9]+)/xhr/$', XHRView.as_view(), name='xhr'),
|
||||
|
||||
@@ -36,13 +36,10 @@ import logging
|
||||
from decimal import Decimal
|
||||
|
||||
from django.contrib import messages
|
||||
from django.core import signing
|
||||
from django.core.cache import cache
|
||||
from django.db import transaction
|
||||
from django.db.models import Sum
|
||||
from django.http import (
|
||||
Http404, HttpResponse, HttpResponseBadRequest, JsonResponse,
|
||||
)
|
||||
from django.http import Http404, HttpResponse, JsonResponse
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
from django.urls import reverse
|
||||
from django.utils.decorators import method_decorator
|
||||
@@ -104,21 +101,6 @@ class PaypalOrderView:
|
||||
}) + ('?paid=yes' if self.order.status == Order.STATUS_PAID else ''))
|
||||
|
||||
|
||||
@xframe_options_exempt
|
||||
def redirect_view(request, *args, **kwargs):
|
||||
signer = signing.Signer(salt='safe-redirect')
|
||||
try:
|
||||
url = signer.unsign(request.GET.get('url', ''))
|
||||
except signing.BadSignature:
|
||||
return HttpResponseBadRequest('Invalid parameter')
|
||||
|
||||
r = render(request, 'pretixplugins/paypal2/redirect.html', {
|
||||
'url': url,
|
||||
})
|
||||
r._csp_ignore = True
|
||||
return r
|
||||
|
||||
|
||||
@method_decorator(csrf_exempt, name='dispatch')
|
||||
@method_decorator(xframe_options_exempt, 'dispatch')
|
||||
class XHRView(View):
|
||||
|
||||
@@ -46,7 +46,6 @@ import stripe
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.core import signing
|
||||
from django.db import transaction
|
||||
from django.http import HttpRequest
|
||||
from django.template.loader import get_template
|
||||
@@ -72,6 +71,7 @@ from pretix.base.payment import (
|
||||
)
|
||||
from pretix.base.plugins import get_all_plugins
|
||||
from pretix.base.settings import SettingsSandbox
|
||||
from pretix.base.views.redirect import safelink
|
||||
from pretix.helpers import OF_SELF
|
||||
from pretix.helpers.countries import CachedCountries
|
||||
from pretix.helpers.http import get_client_ip
|
||||
@@ -745,15 +745,7 @@ class StripeMethod(BasePaymentProvider):
|
||||
|
||||
def redirect(self, request, url):
|
||||
if request.session.get('iframe_session', False):
|
||||
return (
|
||||
eventreverse_absolute(request.event, 'plugins:stripe:redirect') +
|
||||
'?data=' + signing.dumps({
|
||||
'url': url,
|
||||
'session': {
|
||||
'payment_stripe_order_secret': request.session['payment_stripe_order_secret'],
|
||||
},
|
||||
}, salt='safe-redirect')
|
||||
)
|
||||
return safelink(url, framebreak=True)
|
||||
else:
|
||||
return str(url)
|
||||
|
||||
@@ -1053,11 +1045,7 @@ class StripeMethod(BasePaymentProvider):
|
||||
'hash': payment.order.tagged_secret('plugins:stripe'),
|
||||
})
|
||||
if not self.redirect_in_widget_allowed and request.session.get('iframe_session', False):
|
||||
return eventreverse_absolute(self.event, 'plugins:stripe:redirect') + '?data=' + signing.dumps({
|
||||
'url': url,
|
||||
'session': {},
|
||||
}, salt='safe-redirect')
|
||||
|
||||
return safelink(url, framebreak=True)
|
||||
return url
|
||||
|
||||
def _confirm_payment_intent(self, request, payment):
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
{% load compress %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{ settings.PRETIX_INSTANCE_NAME }}</title>
|
||||
{% compress css %}
|
||||
<link rel="stylesheet" type="text/x-scss" href="{% static "pretixbase/scss/cachedfiles.scss" %}"/>
|
||||
{% endcompress %}
|
||||
{% compress js %}
|
||||
<script type="text/javascript" src="{% static "jquery/js/jquery-3.6.4.min.js" %}"></script>
|
||||
{% endcompress %}
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>{% trans "The payment process has started in a new window." %}</h1>
|
||||
|
||||
<p>
|
||||
{% trans "The window to enter your payment data was not opened or was closed?" %}
|
||||
</p>
|
||||
<p>
|
||||
<a href="{{ url }}" target="_blank" class="btn btn-default btn-lg">
|
||||
<span class="fa fa-external-link-square"></span>
|
||||
{% trans "Click here in order to open the window." %}
|
||||
</a>
|
||||
</p>
|
||||
<script>
|
||||
window.open('{{ url|escapejs }}');
|
||||
</script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -25,13 +25,12 @@ from pretix.multidomain import event_url
|
||||
|
||||
from .views import (
|
||||
OrganizerSettingsFormView, ReturnView, ScaReturnView, ScaView,
|
||||
oauth_disconnect, oauth_return, redirect_view, webhook,
|
||||
oauth_disconnect, oauth_return, webhook,
|
||||
)
|
||||
|
||||
event_patterns = [
|
||||
re_path(r'^stripe/', include([
|
||||
event_url(r'^webhook/$', webhook, name='webhook', require_live=False),
|
||||
re_path(r'^redirect/$', redirect_view, name='redirect'),
|
||||
re_path(r'^return/(?P<order>[^/]+)/(?P<hash>[^/]+)/(?P<payment>[0-9]+)/$', ReturnView.as_view(), name='return'),
|
||||
re_path(r'^sca/(?P<order>[^/]+)/(?P<hash>[^/]+)/(?P<payment>[0-9]+)/$', ScaView.as_view(), name='sca'),
|
||||
re_path(r'^sca/(?P<order>[^/]+)/(?P<hash>[^/]+)/(?P<payment>[0-9]+)/return/$',
|
||||
|
||||
@@ -34,13 +34,11 @@
|
||||
|
||||
import json
|
||||
import logging
|
||||
import urllib.parse
|
||||
|
||||
import requests
|
||||
from django.contrib import messages
|
||||
from django.core import signing
|
||||
from django.db import transaction
|
||||
from django.http import Http404, HttpResponse, HttpResponseBadRequest
|
||||
from django.http import Http404, HttpResponse
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
from django.urls import reverse
|
||||
from django.utils.decorators import method_decorator
|
||||
@@ -64,7 +62,7 @@ from pretix.control.views.event import DecoupleMixin
|
||||
from pretix.control.views.organizer import OrganizerDetailViewMixin
|
||||
from pretix.helpers import OF_SELF
|
||||
from pretix.helpers.http import redirect_to_url
|
||||
from pretix.multidomain.urlreverse import eventreverse, eventreverse_absolute
|
||||
from pretix.multidomain.urlreverse import eventreverse
|
||||
from pretix.plugins.stripe.forms import OrganizerStripeSettingsForm
|
||||
from pretix.plugins.stripe.models import ReferencedStripeObject
|
||||
from pretix.plugins.stripe.tasks import (
|
||||
@@ -74,28 +72,6 @@ from pretix.plugins.stripe.tasks import (
|
||||
logger = logging.getLogger('pretix.plugins.stripe')
|
||||
|
||||
|
||||
@xframe_options_exempt
|
||||
def redirect_view(request, *args, **kwargs):
|
||||
try:
|
||||
data = signing.loads(request.GET.get('data', ''), salt='safe-redirect')
|
||||
except signing.BadSignature:
|
||||
return HttpResponseBadRequest('Invalid parameter')
|
||||
|
||||
if 'go' in request.GET:
|
||||
if 'session' in data:
|
||||
for k, v in data['session'].items():
|
||||
request.session[k] = v
|
||||
return redirect(data['url'])
|
||||
else:
|
||||
params = request.GET.copy()
|
||||
params['go'] = '1'
|
||||
r = render(request, 'pretixplugins/stripe/redirect.html', {
|
||||
'url': eventreverse_absolute(request.event, 'plugins:stripe:redirect') + '?' + urllib.parse.urlencode(params),
|
||||
})
|
||||
r._csp_ignore = True
|
||||
return r
|
||||
|
||||
|
||||
@scopes_disabled()
|
||||
def oauth_return(request, *args, **kwargs):
|
||||
import stripe
|
||||
@@ -514,11 +490,6 @@ class StripeOrderView:
|
||||
return self.request.event.get_payment_providers()[self.payment.provider]
|
||||
|
||||
def _redirect_to_order(self):
|
||||
if self.request.session.get('payment_stripe_order_secret') != self.order.secret and not self.payment.provider.startswith('stripe'):
|
||||
messages.error(self.request, _('Sorry, there was an error in the payment process. Please check the link '
|
||||
'in your emails to continue.'))
|
||||
return redirect_to_url(eventreverse(self.request.event, 'presale:event.index'))
|
||||
|
||||
return redirect_to_url(eventreverse(self.request.event, 'presale:event.order', kwargs={
|
||||
'order': self.order.code,
|
||||
'secret': self.order.secret
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
{% load i18n %}
|
||||
{% load eventurl %}
|
||||
{% load urlreplace %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
{% if cart_namespace %}
|
||||
@@ -23,9 +24,8 @@
|
||||
class="btn btn-primary btn-lg" target="_blank">
|
||||
{% trans "Continue in new tab" %}
|
||||
</a>
|
||||
<script>
|
||||
window.open('{{ url|escapejs }}');
|
||||
</script>
|
||||
{{ url|json_script:"framebreak-url" }}
|
||||
<script type="text/javascript" src="{% static "pretixbase/js/framebreak.js" %}"></script>
|
||||
</div>
|
||||
{% else %}
|
||||
<h1>{% trans "Cookies not supported" %}</h1>
|
||||
|
||||
@@ -536,7 +536,6 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
|
||||
**pass_through_url_params,
|
||||
})
|
||||
})
|
||||
r._csp_ignore = True
|
||||
return r
|
||||
|
||||
if not request.event.all_sales_channels and request.sales_channel.identifier not in (s.identifier for s in request.event.limit_sales_channels.all()):
|
||||
|
||||
@@ -125,7 +125,6 @@ class WaitingView(EventViewMixin, FormView):
|
||||
request.event, "presale:event.waitinglist", kwargs={'cart_namespace': kwargs.get('cart_namespace')}
|
||||
) + '?' + url_replace(request, 'require_cookie', '', 'iframe', '', 'locale', request.GET.get('locale', get_language_without_region()))
|
||||
})
|
||||
r._csp_ignore = True
|
||||
return r
|
||||
|
||||
if not self.itemvars:
|
||||
|
||||
@@ -624,6 +624,9 @@ LOGGING = {
|
||||
'request_id': {
|
||||
'()': 'pretix.helpers.logs.RequestIdFilter'
|
||||
},
|
||||
'skip_not_found': {
|
||||
'()': 'pretix.helpers.logs.SkipNotFoundFilter',
|
||||
}
|
||||
},
|
||||
'handlers': {
|
||||
'console': {
|
||||
@@ -665,6 +668,7 @@ LOGGING = {
|
||||
'handlers': ['file', 'console', 'mail_admins'],
|
||||
'level': loglevel,
|
||||
'propagate': True,
|
||||
'filters': ['skip_not_found'],
|
||||
},
|
||||
'pretix.security.csp': {
|
||||
'handlers': ['csp_file'],
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
// Attempt to auto-open page in new tab. Will be ignored by most browser's popup blockers anyways, though.
|
||||
var url = JSON.parse(document.getElementById('framebreak-url').innerText)
|
||||
window.open(url)
|
||||
@@ -119,7 +119,7 @@ def test_linkify_abs(link):
|
||||
assert markdown_compile_email(input) == f"<p>{output}</p>"
|
||||
|
||||
|
||||
signer = signing.Signer(salt='safe-redirect')
|
||||
signer = signing.Signer(salt='safelink-url')
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
#
|
||||
# This file is part of pretix (Community Edition).
|
||||
#
|
||||
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||
# Copyright (C) 2020-today pretix 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/>.
|
||||
#
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
|
||||
def test_start_with_redis_down():
|
||||
"""
|
||||
This is a test that ensures that pretix is able to start without a running redis server,
|
||||
even if one is configured.
|
||||
"""
|
||||
with tempfile.NamedTemporaryFile(suffix="cfg") as f:
|
||||
f.write(b"[redis]\nlocation=redis://127.0.0.99:65534/2\n")
|
||||
f.flush()
|
||||
|
||||
assert subprocess.check_call(
|
||||
[
|
||||
sys.executable,
|
||||
os.path.join(os.path.dirname(__file__), '../manage.py'),
|
||||
"noop",
|
||||
],
|
||||
env={
|
||||
"PRETIX_CONFIG_FILE": f.name,
|
||||
}
|
||||
) == 0
|
||||
Reference in New Issue
Block a user