Compare commits

..

5 Commits

Author SHA1 Message Date
Martin Gross
451ed221d9 Settings: Add option to disable walletdetection 2023-07-11 11:05:09 +02:00
Richard Schreiber
b9af34f0fd convert walletdetection to promise with loading indicator 2023-07-05 14:18:55 +02:00
Martin Gross
fc15811b7f isort 2023-06-30 11:32:11 +02:00
Martin Gross
fdb2a20514 Add documentation 2023-06-30 10:47:28 +02:00
Martin Gross
1b1c4358d3 PProv: Implement detection of wallets such as Google Pay and Apple Pay 2023-06-30 10:21:22 +02:00
44 changed files with 1918 additions and 30427 deletions

View File

@@ -152,10 +152,6 @@ Example::
password=abcd
host=localhost
port=3306
sslmode=require
sslrootcert=/etc/pretix/postgresql-ca.crt
sslcert=/etc/pretix/postgresql-client-crt.crt
sslkey=/etc/pretix/postgresql-client-key.key
``backend``
One of ``sqlite3`` and ``postgresql``.
@@ -167,11 +163,6 @@ Example::
``user``, ``password``, ``host``, ``port``
Connection details for the database connection. Empty by default.
``sslmode``, ``sslrootcert``
Connection TLS details for the PostgreSQL database connection. Possible values of ``sslmode`` are ``disable``, ``allow``, ``prefer``, ``require``, ``verify-ca``, and ``verify-full``. ``sslrootcert`` should be the accessible path of the ca certificate. Both values are empty by default.
``sslcert``, ``sslkey``
Connection mTLS details for the PostgreSQL database connection. It's also necessary to specify ``sslmode`` and ``sslrootcert`` parameters, please check the correct values from the TLS part. ``sslcert`` should be the accessible path of the client certificate. ``sslkey`` should be the accessible path of the client key. All values are empty by default.
.. _`config-replica`:
Database replica settings
@@ -333,10 +324,6 @@ to speed up various operations::
["sentinel_host_3", 26379]
]
password=password
ssl_cert_reqs=required
ssl_ca_certs=/etc/pretix/redis-ca.pem
ssl_keyfile=/etc/pretix/redis-client-crt.pem
ssl_certfile=/etc/pretix/redis-client-key.key
``location``
The location of redis, as a URL of the form ``redis://[:password]@localhost:6379/0``
@@ -360,22 +347,6 @@ to speed up various operations::
If your redis setup doesn't require a password or you already specified it in the location you can omit this option.
If this is set it will be passed to redis as the connection option PASSWORD.
``ssl_cert_reqs``
If this is set it will be passed to redis as the connection option ``SSL_CERT_REQS``.
Possible values are ``none``, ``optional``, and ``required``.
``ssl_ca_certs``
If your redis setup doesn't require TLS you can omit this option.
If this is set it will be passed to redis as the connection option ``SSL_CA_CERTS``. Possible value is the ca path.
``ssl_keyfile``
If your redis setup doesn't require mTLS you can omit this option.
If this is set it will be passed to redis as the connection option ``SSL_KEYFILE``. Possible value is the keyfile path.
``ssl_certfile``
If your redis setup doesn't require mTLS you can omit this option.
If this is set it will be passed to redis as the connection option ``SSL_CERTFILE``. Possible value is the certfile path.
If redis is not configured, pretix will store sessions and locks in the database. If memcached
is configured, memcached will be used for caching instead of redis.
@@ -425,8 +396,6 @@ The two ``transport_options`` entries can be omitted in most cases.
If they are present they need to be a valid JSON dictionary.
For possible entries in that dictionary see the `Celery documentation`_.
It is possible the use Redis with TLS/mTLS for the broker or the backend. To do so, it is necessary to specify the TLS identifier ``rediss``, the ssl mode ``ssl_cert_reqs`` and optionally specify the CA (TLS) ``ssl_ca_certs``, cert ``ssl_certfile`` and key ``ssl_keyfile`` (mTLS) path as encoded string. the following uri describes the format and possible parameters ``rediss://0.0.0.0:6379/1?ssl_cert_reqs=required&ssl_ca_certs=%2Fetc%2Fpretix%2Fredis-ca.pem&ssl_certfile=%2Fetc%2Fpretix%2Fredis-client-crt.pem&ssl_keyfile=%2Fetc%2Fpretix%2Fredis-client-key.key``
To use redis with sentinels set the broker or backend to ``sentinel://sentinel_host_1:26379;sentinel_host_2:26379/0``
and the respective transport_options to ``{"master_name":"mymaster"}``.
If your redis instances behind the sentinel have a password use ``sentinel://:my_password@sentinel_host_1:26379;sentinel_host_2:26379/0``.

View File

@@ -61,7 +61,7 @@ Backend
item_formsets, order_search_filter_q, order_search_forms
.. automodule:: pretix.base.signals
:members: logentry_display, logentry_object_link, requiredaction_display, timeline_events, orderposition_blocked_display, customer_created, customer_signed_in
:members: logentry_display, logentry_object_link, requiredaction_display, timeline_events, orderposition_blocked_display
Vouchers
""""""""

View File

@@ -945,7 +945,6 @@ with scopes_disabled():
| Q(addon_to__attendee_email__icontains=value)
| Q(order__code__istartswith=value)
| Q(order__invoice_address__name_cached__icontains=value)
| Q(order__invoice_address__company__icontains=value)
| Q(order__email__icontains=value)
| Q(pk__in=matching_media)
)

View File

@@ -140,7 +140,7 @@ class BaseExporter:
"""
return {}
def render(self, form_data: dict) -> Tuple[str, str, Optional[bytes]]:
def render(self, form_data: dict) -> Tuple[str, str, bytes]:
"""
Render the exported file and return a tuple consisting of a filename, a file type
and file content.

View File

@@ -26,7 +26,7 @@ from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
from django.conf import settings
from django.http import Http404, HttpRequest, HttpResponse
from django.middleware.common import CommonMiddleware
from django.urls import get_script_prefix, resolve
from django.urls import get_script_prefix
from django.utils import timezone, translation
from django.utils.cache import patch_vary_headers
from django.utils.deprecation import MiddlewareMixin
@@ -230,8 +230,6 @@ class SecurityMiddleware(MiddlewareMixin):
)
def process_response(self, request, resp):
url = resolve(request.path_info)
if settings.DEBUG and resp.status_code >= 400:
# Don't use CSP on debug error page as it breaks of Django's fancy error
# pages
@@ -251,26 +249,20 @@ class SecurityMiddleware(MiddlewareMixin):
h = {
'default-src': ["{static}"],
'script-src': ['{static}'],
'script-src': ['{static}', 'https://checkout.stripe.com', 'https://js.stripe.com', 'https://pay.google.com'],
'object-src': ["'none'"],
'frame-src': ['{static}'],
'frame-src': ['{static}', 'https://checkout.stripe.com', 'https://js.stripe.com'],
'style-src': ["{static}", "{media}"],
'connect-src': ["{dynamic}", "{media}"],
'img-src': ["{static}", "{media}", "data:"] + img_src,
'connect-src': ["{dynamic}", "{media}", "https://checkout.stripe.com"],
'img-src': ["{static}", "{media}", "data:", "https://*.stripe.com"] + img_src,
'font-src': ["{static}"],
'media-src': ["{static}", "data:"],
# form-action is not only used to match on form actions, but also on URLs
# form-actions redirect to. In the context of e.g. payment providers or
# single-sign-on this can be nearly anything, so we cannot really restrict
# single-sign-on this can be nearly anything so we cannot really restrict
# this. However, we'll restrict it to HTTPS.
'form-action': ["{dynamic}", "https:"] + (['http:'] if settings.SITE_URL.startswith('http://') else []),
}
# Only include pay.google.com for wallet detection purposes on the Payment selection page
if (
url.url_name == "event.order.pay.change" or
(url.url_name == "event.checkout" and url.kwargs['step'] == "payment")
):
h['script-src'].append('https://pay.google.com')
if settings.LOG_CSP:
h['report-uri'] = ["/csp_report/"]
if 'Content-Security-Policy' in resp:

View File

@@ -48,7 +48,6 @@ from functools import partial
from io import BytesIO
import jsonschema
import reportlab.rl_config
from bidi.algorithm import get_display
from django.conf import settings
from django.contrib.staticfiles import finders
@@ -61,8 +60,7 @@ from django.utils.html import conditional_escape
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _, pgettext
from i18nfield.strings import LazyI18nString
from pypdf import PdfReader, PdfWriter, Transformation
from pypdf.generic import RectangleObject
from pypdf import PdfReader
from reportlab.graphics import renderPDF
from reportlab.graphics.barcode.qr import QrCodeWidget
from reportlab.graphics.shapes import Drawing
@@ -87,9 +85,6 @@ from pretix.presale.style import get_fonts
logger = logging.getLogger(__name__)
if not settings.DEBUG:
reportlab.rl_config.shapeChecking = 0
DEFAULT_VARIABLES = OrderedDict((
("secret", {
@@ -866,7 +861,7 @@ class Renderer:
if image_file:
try:
ir = ThumbnailingImageReader(image_file.path)
ir = ThumbnailingImageReader(image_file)
ir.resize(float(o['width']) * mm, float(o['height']) * mm, 300)
canvas.drawImage(
image=ir,
@@ -997,10 +992,7 @@ class Renderer:
elif o['type'] == "poweredby":
self._draw_poweredby(canvas, op, o)
if self.bg_pdf:
page_size = (
self.bg_pdf.pages[0].mediabox[2] - self.bg_pdf.pages[0].mediabox[0],
self.bg_pdf.pages[0].mediabox[3] - self.bg_pdf.pages[0].mediabox[1]
)
page_size = (self.bg_pdf.pages[0].mediabox[2], self.bg_pdf.pages[0].mediabox[3])
if self.bg_pdf.pages[0].get('/Rotate') in (90, 270):
# swap dimensions due to pdf being rotated
page_size = page_size[::-1]
@@ -1028,12 +1020,14 @@ class Renderer:
with open(os.path.join(d, 'out.pdf'), 'rb') as f:
return BytesIO(f.read())
else:
from pypdf import PdfReader, PdfWriter, Transformation
from pypdf.generic import RectangleObject
buffer.seek(0)
new_pdf = PdfReader(buffer)
output = PdfWriter()
for i, page in enumerate(new_pdf.pages):
bg_page = copy.deepcopy(self.bg_pdf.pages[i])
bg_page = copy.copy(self.bg_pdf.pages[i])
bg_rotation = bg_page.get('/Rotate')
if bg_rotation:
# /Rotate is clockwise, transformation.rotate is counter-clockwise
@@ -1070,56 +1064,6 @@ class Renderer:
return outbuffer
def merge_background(fg_pdf, bg_pdf, out_file, compress):
if settings.PDFTK:
with tempfile.TemporaryDirectory() as d:
fg_filename = os.path.join(d, 'fg.pdf')
bg_filename = os.path.join(d, 'bg.pdf')
fg_pdf.write(fg_filename)
bg_pdf.write(bg_filename)
pdftk_cmd = [
settings.PDFTK,
fg_filename,
'multibackground',
bg_filename,
'output',
'-',
]
if compress:
pdftk_cmd.append('compress')
subprocess.run(pdftk_cmd, check=True, stdout=out_file)
else:
output = PdfWriter()
for i, page in enumerate(fg_pdf.pages):
bg_page = copy.deepcopy(bg_pdf.pages[i])
bg_rotation = bg_page.get('/Rotate')
if bg_rotation:
# /Rotate is clockwise, transformation.rotate is counter-clockwise
t = Transformation().rotate(bg_rotation)
w = float(page.mediabox.getWidth())
h = float(page.mediabox.getHeight())
if bg_rotation in (90, 270):
# offset due to rotation base
if bg_rotation == 90:
t = t.translate(h, 0)
else:
t = t.translate(0, w)
# rotate mediabox as well
page.mediabox = RectangleObject((
page.mediabox.left.as_numeric(),
page.mediabox.bottom.as_numeric(),
page.mediabox.top.as_numeric(),
page.mediabox.right.as_numeric(),
))
page.trimbox = page.mediabox
elif bg_rotation == 180:
t = t.translate(w, h)
page.add_transformation(t)
bg_page.merge_page(page)
output.add_page(bg_page)
output.write(out_file)
@deconstructible
class PdfLayoutValidator:
def __call__(self, value):

View File

@@ -86,8 +86,8 @@ def _build_time(t=None, value=None, ev=None, now_dt=None):
return ev.date_admission or ev.date_from
def _logic_annotate_for_graphic_explain(rules, ev, rule_data, now_dt):
logic_environment = _get_logic_environment(ev, now_dt)
def _logic_annotate_for_graphic_explain(rules, ev, rule_data):
logic_environment = _get_logic_environment(ev)
event = ev if isinstance(ev, Event) else ev.event
def _evaluate_inners(r):
@@ -152,7 +152,7 @@ def _logic_explain(rules, ev, rule_data, now_dt=None):
get in before 17:00". In the middle of the night it would switch to "You can only get in after 09:00".
"""
now_dt = now_dt or now()
logic_environment = _get_logic_environment(ev, now_dt)
logic_environment = _get_logic_environment(ev)
_var_values = {'False': False, 'True': True}
_var_explanations = {}
@@ -229,7 +229,7 @@ def _logic_explain(rules, ev, rule_data, now_dt=None):
for vname, data in _var_explanations.items():
var, operator, rhs = data['var'], data['operator'], data['rhs']
if var == 'now':
compare_to = _build_time(*rhs[0]['buildTime'], ev=ev, now_dt=now_dt).astimezone(ev.timezone)
compare_to = _build_time(*rhs[0]['buildTime'], ev=ev).astimezone(ev.timezone)
tolerance = timedelta(minutes=float(rhs[1])) if len(rhs) > 1 and rhs[1] else timedelta(seconds=0)
if operator == 'isBefore':
compare_to += tolerance
@@ -337,7 +337,7 @@ def _logic_explain(rules, ev, rule_data, now_dt=None):
return ', '.join(var_texts[v] for v in paths_with_min_weight[0] if not _var_values[v])
def _get_logic_environment(ev, now_dt):
def _get_logic_environment(ev):
# Every change to our supported JSON logic must be done
# * in pretix.base.services.checkin
# * in pretix.base.models.checkin
@@ -354,7 +354,7 @@ def _get_logic_environment(ev, now_dt):
logic.add_operation('objectList', lambda *objs: list(objs))
logic.add_operation('lookup', lambda model, pk, str: int(pk))
logic.add_operation('inList', lambda a, b: a in b)
logic.add_operation('buildTime', partial(_build_time, ev=ev, now_dt=now_dt))
logic.add_operation('buildTime', partial(_build_time, ev=ev))
logic.add_operation('isBefore', is_before)
logic.add_operation('isAfter', lambda t1, t2, tol=None: is_before(t2, t1, tol))
return logic
@@ -861,7 +861,7 @@ def perform_checkin(op: OrderPosition, clist: CheckinList, given_answers: dict,
if type == Checkin.TYPE_ENTRY and clist.rules:
rule_data = LazyRuleVars(op, clist, dt)
logic = _get_logic_environment(op.subevent or clist.event, now_dt=dt)
logic = _get_logic_environment(op.subevent or clist.event)
if not logic.apply(clist.rules, rule_data):
if force:
force_used = True

View File

@@ -26,7 +26,7 @@ from typing import Any, Dict, Union
from celery.exceptions import MaxRetriesExceededError
from django.conf import settings
from django.core.files.base import ContentFile
from django.db import close_old_connections, connection, transaction
from django.db import connection, transaction
from django.dispatch import receiver
from django.utils.timezone import now, override
from django.utils.translation import gettext
@@ -86,12 +86,9 @@ def export(self, event: Event, fileid: str, provider: str, form_data: Dict[str,
gettext('Your export did not contain any data.')
)
file.filename, file.type, data = d
close_old_connections() # This task can run very long, we might need a new DB connection
f = ContentFile(data)
file.file.save(cachedfile_name(file, file.filename), f)
return str(file.pk)
return file.pk
@app.task(base=ProfiledOrganizerUserTask, throws=(ExportError,), bind=True)
@@ -157,12 +154,9 @@ def multiexport(self, organizer: Organizer, user: User, device: int, token: int,
gettext('Your export did not contain any data.')
)
file.filename, file.type, data = d
close_old_connections() # This task can run very long, we might need a new DB connection
f = ContentFile(data)
file.file.save(cachedfile_name(file, file.filename), f)
return str(file.pk)
return file.pk
def _run_scheduled_export(schedule, context: Union[Event, Organizer], exporter, config_url, retry_func, has_permission):
@@ -220,11 +214,6 @@ def _run_scheduled_export(schedule, context: Union[Event, Organizer], exporter,
raise ExportError(
gettext('Your exported data exceeded the size limit for scheduled exports.')
)
conn = transaction.get_connection()
if not conn.in_atomic_block: # atomic execution only happens during tests or with celery always_eager on
close_old_connections() # This task can run very long, we might need a new DB connection
f = ContentFile(data)
file.file.save(cachedfile_name(file, file.filename), f)
except ExportEmptyError as e:

View File

@@ -787,23 +787,3 @@ return a dictionary mapping names of attributes in the settings store to DRF ser
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
customer_created = GlobalSignal()
"""
Arguments: ``customer``
This signal is sent out every time a customer account is created. The ``customer``
object is given as the first argument.
The ``sender`` keyword argument will contain the organizer.
"""
customer_signed_in = GlobalSignal()
"""
Arguments: ``customer``
This signal is sent out every time a customer signs in. The ``customer`` object
is given as the first argument.
The ``sender`` keyword argument will contain the organizer.
"""

View File

@@ -20,7 +20,7 @@
</div>
<div class="panel-body form-horizontal">
{% if spf_warning %}
<div class="alert alert-danger">
<div class="alert alert-warning">
<p>
{{ spf_warning }}
</p>
@@ -70,18 +70,10 @@
</div>
</div>
{% if spf_warning %}
<div class="form-group submit-group">
<a href="" class="btn btn-default btn-save">
{% trans "Cancel" %}
</a>
</div>
{% else %}
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}
</button>
</div>
{% endif %}
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}
</button>
</div>
</form>
{% endblock %}

View File

@@ -17,7 +17,7 @@
placeholder="{% trans "Prefix (optional)" %}">
<div class="input-group">
<input type="number" class="form-control input-xs"
id="voucher-bulk-codes-num" max="100000"
id="voucher-bulk-codes-num"
placeholder="{% trans "Number" context "number_of_things" %}">
<div class="input-group-btn">
<button class="btn btn-default" type="button" id="voucher-bulk-codes-generate"

View File

@@ -212,21 +212,11 @@
<span class="label label-warning">{% trans "Voucher assigned" %}</span>
{% endif %}
{% elif e.availability.0 == 100 %}
{% if e.availability.1|default_if_none:"none" == "none" %}
<span class="label label-danger" data-toggle="tooltip"
title="{% trans "For safety reasons, the waiting list does not run if the quota is set to unlimited." %}">
<span class="fa fa-ban" aria-hidden="true"></span>
{% blocktrans trimmed %}
Quota unlimited
{% endblocktrans %}
</span>
{% else %}
<span class="label label-warning">
{% blocktrans with num=e.availability.1 %}
Waiting, product {{ num }}x available
{% endblocktrans %}
</span>
{% endif %}
<span class="label label-warning">
{% blocktrans with num=e.availability.1 %}
Waiting, product {{ num }}x available
{% endblocktrans %}
</span>
{% else %}
<span class="label label-danger">{% trans "Waiting, product unavailable" %}</span>
{% endif %}
@@ -236,7 +226,7 @@
<a href="{% url "control:event.voucher" organizer=request.event.organizer.slug event=request.event.slug voucher=e.voucher.pk %}">
{{ e.voucher }}
</a>
{% elif not e.voucher and e.availability.0 == 100 and e.availability.1|default_if_none:"none" != "none" %}
{% elif not e.voucher and e.availability.0 == 100 %}
<button name="assign" value="{{ e.pk }}" class="btn btn-default btn-xs">
{% trans "Send a voucher" %}
</button>

View File

@@ -530,8 +530,7 @@ class CheckInListSimulator(EventPermissionRequiredMixin, FormView):
and (self.result["status"] in ("ok", "incomplete") or self.result["reason"] == "rules"):
op = OrderPosition.objects.get(pk=self.result["position"]["id"])
rule_data = LazyRuleVars(op, self.list, form.cleaned_data["datetime"])
rule_graph = _logic_annotate_for_graphic_explain(self.list.rules, op.subevent or self.list.event, rule_data,
form.cleaned_data["datetime"])
rule_graph = _logic_annotate_for_graphic_explain(self.list.rules, op.subevent or self.list.event, rule_data)
self.result["rule_graph"] = rule_graph
if self.result.get("questions"):

View File

@@ -192,8 +192,8 @@ class MailSettingsSetupView(TemplateView):
spf_record = get_spf_record(hostname)
if not spf_record:
spf_warning = _(
'We could not find an SPF record set for the domain you are trying to use. This means that '
'there is a very high change most of the emails will be rejected or markes as spam. We '
'We could not find an SPF record set for the domain you are trying to use. You can still '
'proceed, but it will increase the chance of emails going to spam or being rejected. We '
'strongly recommend setting an SPF record on the domain. You can do so through the DNS '
'settings at the provider you registered your domain with.'
)
@@ -205,8 +205,7 @@ class MailSettingsSetupView(TemplateView):
'this system in the SPF record.'
)
verification = settings.MAIL_CUSTOM_SENDER_VERIFICATION_REQUIRED and not spf_warning
if verification:
if settings.MAIL_CUSTOM_SENDER_VERIFICATION_REQUIRED:
if 'verification' in self.request.POST:
messages.error(request, _('The verification code was incorrect, please try again.'))
else:
@@ -231,7 +230,7 @@ class MailSettingsSetupView(TemplateView):
context={
'basetpl': self.basetpl,
'object': self.object,
'verification': verification,
'verification': settings.MAIL_CUSTOM_SENDER_VERIFICATION_REQUIRED,
'spf_warning': spf_warning,
'spf_record': spf_record,
'spf_key': settings.MAIL_CUSTOM_SENDER_SPF_STRING,

View File

@@ -569,8 +569,6 @@ class VoucherRNG(EventPermissionRequiredMixin, View):
def get(self, request, *args, **kwargs):
try:
num = int(request.GET.get('num', '5'))
if num > 100_000:
return HttpResponseBadRequest()
except ValueError: # NOQA
return HttpResponseBadRequest()

View File

@@ -91,7 +91,7 @@ class WaitingListQuerySetMixin:
return self.request.POST
return self.request.GET
def get_queryset(self, force_filtered=False):
def get_queryset(self):
qs = WaitingListEntry.objects.filter(
event=self.request.event
).select_related('item', 'variation', 'voucher').prefetch_related(
@@ -135,8 +135,6 @@ class WaitingListQuerySetMixin:
qs = qs.filter(
id__in=self.request_data.getlist('entry')
)
elif force_filtered and '__ALL' not in self.request_data:
qs = qs.none()
return qs
@@ -160,7 +158,7 @@ class WaitingListActionView(EventPermissionRequiredMixin, WaitingListQuerySetMix
'forbidden': self.get_queryset().filter(voucher__isnull=False),
})
elif request.POST.get('action') == 'delete_confirm':
for obj in self.get_queryset(force_filtered=True):
for obj in self.get_queryset():
if not obj.voucher_id:
obj.log_action('pretix.event.orders.waitinglist.deleted', user=self.request.user)
obj.delete()

View File

@@ -8,16 +8,16 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-06-27 12:51+0000\n"
"PO-Revision-Date: 2023-07-11 11:38+0000\n"
"Last-Translator: hara metaxa <metaxahara@gmail.com>\n"
"Language-Team: Greek <https://translate.pretix.eu/projects/pretix/pretix/el/>"
"\n"
"PO-Revision-Date: 2023-03-07 03:00+0000\n"
"Last-Translator: alemao8 <alevizosfotis@gmail.com>\n"
"Language-Team: Greek <https://translate.pretix.eu/projects/pretix/pretix/el/"
">\n"
"Language: el\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.17\n"
"X-Generator: Weblate 4.15.2\n"
#: pretix/_base_settings.py:78
msgid "English"
@@ -31444,13 +31444,17 @@ msgid "Cart expired"
msgstr "Το καλάθι έληξε"
#: pretix/presale/templates/pretixpresale/event/checkout_base.html:37
#, fuzzy
#| msgid "Show information"
msgid "Show full cart"
msgstr "Εμφάνιση καλαθιού"
msgstr "Εμφάνιση πληροφοριών"
#: pretix/presale/templates/pretixpresale/event/checkout_base.html:49
#: pretix/presale/templates/pretixpresale/event/index.html:78
#, fuzzy
#| msgid "This file is from a different event."
msgid "Add tickets for a different date"
msgstr "Προσθέστε εισιτήρια για διαφορετική ημερομηνία"
msgstr "Αυτό το αρχείο προέρχεται από διαφορετική εκδήλωση."
#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:7
#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:9
@@ -31845,8 +31849,10 @@ msgstr "από %(minprice)s"
#: pretix/presale/templates/pretixpresale/event/fragment_addon_choice.html:97
#: pretix/presale/templates/pretixpresale/event/fragment_product_list.html:89
#, fuzzy
#| msgid "Show variants"
msgid "Hide variants"
msgstr "Απόκρυψη παραλλαγών"
msgstr "Εμφάνιση παραλλαγών"
#: pretix/presale/templates/pretixpresale/event/fragment_addon_choice.html:99
#: pretix/presale/templates/pretixpresale/event/fragment_product_list.html:91
@@ -32191,12 +32197,12 @@ msgstr ""
"Τα στοιχεία του καλαθιού σας είναι στη διάθεσή σας για %(minutes)s λεπτά."
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:486
#, fuzzy
#| msgid "The items in your cart are no longer reserved for you."
msgid ""
"The items in your cart are no longer reserved for you. You can still "
"complete your order as long as theyre available."
msgstr ""
"Τα αντικείμενα στο καλάθι σας δεν είναι πλέον διαθέσιμα. Μπορείτε να "
"επαναλάβετε την διαδικασία εφόσον είναι διαθέσιμα."
msgstr "Τα αντικείμενα στο καλάθι σας δεν είναι πλέον αποκλειστικά για εσάς."
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:490
msgid "Overview of your ordered products."

File diff suppressed because it is too large Load Diff

View File

@@ -4,8 +4,8 @@ msgstr ""
"Project-Id-Version: 1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-06-27 12:51+0000\n"
"PO-Revision-Date: 2023-07-19 17:00+0000\n"
"Last-Translator: Ronan LE MEILLAT <ronan.le_meillat@highcanfly.club>\n"
"PO-Revision-Date: 2023-06-27 07:11+0000\n"
"Last-Translator: Jonathan Berger <drskullster@gmail.com>\n"
"Language-Team: French <https://translate.pretix.eu/projects/pretix/pretix/fr/"
">\n"
"Language: fr\n"
@@ -528,20 +528,28 @@ msgid "Test-Mode of shop has been deactivated"
msgstr "Le mode de test de la boutique a été désactivé"
#: pretix/api/webhooks.py:339
#, fuzzy
#| msgid "Waiting list entry"
msgid "Waiting list entry added"
msgstr "Ajout d'une inscription sur la liste d'attente"
msgstr "Saisie de la liste d'attente"
#: pretix/api/webhooks.py:343
#, fuzzy
#| msgid "Waiting list entry"
msgid "Waiting list entry changed"
msgstr "Changement d'une inscription sur la liste d'attente"
msgstr "Saisie de la liste d'attente"
#: pretix/api/webhooks.py:347
#, fuzzy
#| msgid "Waiting list entry"
msgid "Waiting list entry deleted"
msgstr "Suppression d'une inscription de la liste d'attente"
msgstr "Saisie de la liste d'attente"
#: pretix/api/webhooks.py:351
#, fuzzy
#| msgid "Waiting list entries"
msgid "Waiting list entry received voucher"
msgstr "Bon dinscription sur la liste dattente reçu"
msgstr "Entrées de liste d'attente"
#: pretix/base/addressvalidation.py:100 pretix/base/addressvalidation.py:103
#: pretix/base/addressvalidation.py:108 pretix/base/forms/questions.py:938
@@ -3398,7 +3406,7 @@ msgstr "Interdit par une règle personnalisée"
#: pretix/base/models/checkin.py:347
msgid "Ticket code revoked/changed"
msgstr "Code du billet révoqué/modifié"
msgstr "Code du billet révoqué/changé"
#: pretix/base/models/checkin.py:348
msgid "Information required"
@@ -3418,7 +3426,7 @@ msgstr "Le code du billet est ambigu dans la liste"
#: pretix/base/models/checkin.py:352
msgid "Server error"
msgstr "Erreur côté serveur"
msgstr "Erreur du serveur"
#: pretix/base/models/checkin.py:353
msgid "Ticket blocked"
@@ -3924,11 +3932,11 @@ msgstr ""
#: pretix/base/models/exports.py:64
msgid "Additional recipients (Cc)"
msgstr "Destinataires supplémentaires (copie)"
msgstr "Destinataires supplémentaires (Cc)"
#: pretix/base/models/exports.py:69
msgid "Additional recipients (Bcc)"
msgstr "Destinataires supplémentaires (copie cachée)"
msgstr "Destinataires supplémentaires (Bcc)"
#: pretix/base/models/exports.py:74 pretix/control/forms/event.py:1045
#: pretix/control/forms/event.py:1107 pretix/control/forms/event.py:1119
@@ -4216,9 +4224,9 @@ msgid ""
"product as an add-on product, but only for fixed bundles!"
msgstr ""
"Si cette option est définie, le produit ne sera vendu que dans le cadre de "
"produits groupés. <strong>Ne cochez pas</strong> cette option si vous "
"souhaitez utiliser ce produit pour des offres groupées, pas en tant que "
"produit complémentaire."
"produits groupés. <strong>Cochez</strong> cette option si vous souhaitez "
"utiliser ce produit pour des offres groupées, pas en tant que produit "
"complémentaire."
#: pretix/base/models/items.py:527
msgid ""
@@ -5522,8 +5530,12 @@ msgid "Seat {number}"
msgstr "Siège {number}"
#: pretix/base/models/tax.py:157
#, fuzzy
#| msgid "Your layout file is not a valid layout. Error message: {}"
msgid "Your set of rules is not valid. Error message: {}"
msgstr "Votre ensemble de règles n'est pas valide. Message d'erreur : {}"
msgstr ""
"Votre fichier de mise en page nest pas une mise en page valide. Message "
"derreur : {}"
#: pretix/base/models/tax.py:168
msgid "Official name"
@@ -6661,8 +6673,9 @@ msgid "Attendee country"
msgstr "Pays du participant"
#: pretix/base/pdf.py:211
#, fuzzy
msgid "Pseudonymization ID (lead scanning)"
msgstr "ID de pseudonymisation (balayage principal)"
msgstr "ID pseudonyme"
#: pretix/base/pdf.py:217 pretix/base/pdf.py:222
msgid "Sample event name"
@@ -8035,8 +8048,11 @@ msgstr ""
"réessayer."
#: pretix/base/services/shredder.py:177
#, fuzzy
#| msgctxt "paypal"
#| msgid "Capture completed."
msgid "Data shredding completed"
msgstr "Déchiquetage de données terminé"
msgstr "Capture terminée."
#: pretix/base/services/stats.py:210
msgid "Uncategorized"
@@ -8746,7 +8762,7 @@ msgstr ""
#: pretix/base/settings.py:934
msgid "Accept late payments"
msgstr "Accepter les retards de paiement"
msgstr "Accepter les paiements en retard"
#: pretix/base/settings.py:935
msgid ""
@@ -11443,23 +11459,6 @@ msgid ""
"\n"
"Your pretix team\n"
msgstr ""
"Bonjour\n"
"\n"
"Nous confirmons par la présente que le travail de déchiquetage de données "
"suivant a été effectué:\n"
"\n"
"Organisateur: %(organizer)s\n"
"\n"
"Evénement: %(event)s\n"
"\n"
"Sélection des données: %(shredders)s\n"
"\n"
"Heure de début: %(start_time)s (les nouvelles données ajoutées après cette "
"heure nont peut-être pas été supprimées)\n"
"\n"
"Sinceres salutations\n"
"\n"
"Votre équipe pretix\n"
#: pretix/base/templates/pretixbase/forms/widgets/portrait_image.html:10
msgid "Upload photo"
@@ -12070,16 +12069,16 @@ msgstr "Saisie de texte libre"
#: pretix/control/forms/event.py:666
msgid "Do not ask"
msgstr "Ne pas demander"
msgstr "Ne pas demandez"
#: pretix/control/forms/event.py:667
msgid "Ask, but do not require input"
msgstr "Demander, mais ne pas exiger de saisie"
msgstr "Demandez, mais navez pas besoin dentrée"
#: pretix/control/forms/event.py:668
#: pretix/control/templates/pretixcontrol/event/settings.html:74
msgid "Ask and require input"
msgstr "Demander et exiger la saisie"
msgstr "Demandez et requérez la saise"
#: pretix/control/forms/event.py:740
msgid ""
@@ -13016,7 +13015,7 @@ msgstr "Appareils révoqués"
#: pretix/control/forms/global_settings.py:59
msgid "Additional footer text"
msgstr "Texte supplémentaire en bas de page"
msgstr "Texte de pied de page supplémentaire"
#: pretix/control/forms/global_settings.py:60
msgid "Will be included as additional text in the footer, site-wide."
@@ -14830,11 +14829,11 @@ msgstr "Un événement a été supprimé."
#: pretix/control/logdisplay.py:375
msgid "A removal process for personal data has been started."
msgstr "Un processus de suppression des données personnelles a été lancé."
msgstr ""
#: pretix/control/logdisplay.py:376
msgid "A removal process for personal data has been completed."
msgstr "Un processus de suppression des données personnelles a été achevé."
msgstr ""
#: pretix/control/logdisplay.py:377
msgid "The order details have been changed."
@@ -15890,7 +15889,7 @@ msgstr "Tous les utilisateurs"
#: pretix/control/templates/pretixcontrol/user/staff_session_list.html:5
#: pretix/control/templates/pretixcontrol/user/staff_session_list.html:7
msgid "Admin sessions"
msgstr "Sessions Admin"
msgstr "Sessions d'administration"
#: pretix/control/navigation.py:427
#: pretix/control/templates/pretixcontrol/global_settings_base.html:5
@@ -18381,7 +18380,7 @@ msgstr ""
"Vous pouvez ici définir un ensemble de propriétés de métadonnées (cest-à-"
"dire des variables) que vous pouvez définir ultérieurement pour vos articles "
"et réutiliser dans des endroits tels que les mises en page de tickets. Cest "
"un gain de temps précieux si vous créez beaucoup d'articles."
"un gain de temps utile si vous créez beaucoup, beaucoup déléments."
#: pretix/control/templates/pretixcontrol/event/settings.html:389
#: pretix/control/templates/pretixcontrol/event/settings.html:417
@@ -19013,7 +19012,7 @@ msgstr "Type de produit"
#: pretix/control/templates/pretixcontrol/item/create.html:25
#: pretix/control/templates/pretixcontrol/item/index.html:33
msgid "Admission product"
msgstr "Produit d'admission"
msgstr "Produit dadmission"
#: pretix/control/templates/pretixcontrol/item/create.html:27
#: pretix/control/templates/pretixcontrol/item/index.html:35
@@ -19276,7 +19275,7 @@ msgstr "Nouvelle variante"
#: pretix/control/templates/pretixcontrol/item/include_variations.html:215
msgid "Add a new variation"
msgstr "Ajouter une nouvelle variation"
msgstr "Ajouter une nouvelle variante"
#: pretix/control/templates/pretixcontrol/item/index.html:153
msgid "Availability"
@@ -19566,7 +19565,7 @@ msgstr "Billet dentrée personnalisé"
#: pretix/control/templates/pretixcontrol/items/index.html:87
msgid "Admission ticket without personalization"
msgstr "Billet d'entrée sans personnalisation"
msgstr "Billet dentrée sans personnalisation"
#: pretix/control/templates/pretixcontrol/items/index.html:95
msgid "Product with variations"
@@ -21599,8 +21598,9 @@ msgstr ""
"événements."
#: pretix/control/templates/pretixcontrol/organizers/customer.html:79
#, fuzzy
msgid "Lifetime spending"
msgstr "Dépenses à vie"
msgstr "Paiement en attente"
#: pretix/control/templates/pretixcontrol/organizers/customer.html:101
#: pretix/control/templates/pretixcontrol/organizers/customer_anonymize.html:39
@@ -23036,9 +23036,6 @@ msgid ""
"Depending on the amount of data in your event, the following step may take a "
"while to complete. We will inform you via email once it has been completed."
msgstr ""
"Selon la quantité de données de votre événement, létape suivante peut "
"prendre un certain temps. Nous vous informerons par e-mail une fois quil "
"aura été complété."
#: pretix/control/templates/pretixcontrol/shredder/index.html:11
msgid ""
@@ -23631,7 +23628,7 @@ msgstr "On"
#: pretix/control/templates/pretixcontrol/user/notifications.html:71
#: pretix/control/templates/pretixcontrol/user/settings.html:24
msgid "Off"
msgstr "Désactivé"
msgstr "Off"
#: pretix/control/templates/pretixcontrol/user/notifications.html:75
msgid "You have no permission to receive this notification"
@@ -26572,8 +26569,8 @@ msgid ""
"We will assign you a personal reference code to use after you completed the "
"order."
msgstr ""
"Nous vous assignerons un code de référence personnel à utiliser après avoir "
"complété la commande."
"Nous vous assignerons un code de référence personnel à utiliser après que "
"vous ayez complété la commande."
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/checkout_confirm.html:35
#, python-format
@@ -27099,8 +27096,9 @@ msgstr ""
"liste denregistrement."
#: pretix/plugins/checkinlists/exporters.py:479
#, fuzzy
msgid "Checked out"
msgstr "Passé en caisse"
msgstr "Paiement"
#: pretix/plugins/checkinlists/exporters.py:479
#: pretix/plugins/checkinlists/exporters.py:670
@@ -27387,11 +27385,11 @@ msgid ""
"payment methods world-wide."
msgstr ""
"Acceptez les paiements avec votre compte PayPal. En plus des paiements "
"PayPal standards, vous pouvez désormais également proposer des paiements "
"dans une variété de méthodes de paiement locales à vos clients telles que "
"giropay, SOFORT, iDEAL et bien dautres - ils nont même pas besoin dun "
"compte PayPal. PayPal est lune des méthodes de paiement les plus populaires "
"au monde."
"PayPal réguliers, vous pouvez désormais également proposer des paiements "
"dans une variété de méthodes de paiement locales telles que giropay, SOFORT, "
"iDEAL et bien dautres à vos clients - ils nont même pas besoin dun compte "
"PayPal. PayPal est lune des méthodes de paiement les plus populaires au "
"monde."
#: pretix/plugins/paypal2/payment.py:95
msgid "PayPal Merchant ID"
@@ -28331,9 +28329,10 @@ msgid "Create a new rule"
msgstr "Créer une nouvelle règle"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:33
#, fuzzy
msgctxt "subevent"
msgid "Sent / Total dates"
msgstr "Envoyés / Dates total"
msgstr "date de début d'événement"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:52
msgid "Next execution:"

View File

@@ -7,7 +7,7 @@ msgstr ""
"Project-Id-Version: French\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-06-27 12:51+0000\n"
"PO-Revision-Date: 2023-07-19 17:00+0000\n"
"PO-Revision-Date: 2023-06-17 09:09+0000\n"
"Last-Translator: Ronan LE MEILLAT <ronan.le_meillat@highcanfly.club>\n"
"Language-Team: French <https://translate.pretix.eu/projects/pretix/pretix-js/"
"fr/>\n"
@@ -281,7 +281,7 @@ msgstr "Entrée non autorisée"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:60
msgid "Ticket code revoked/changed"
msgstr "Code du billet révoqué/modifié"
msgstr "Code du billet révoqué/changé"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:61
msgid "Ticket blocked"

File diff suppressed because it is too large Load Diff

View File

@@ -7,10 +7,10 @@ msgstr ""
"Project-Id-Version: 1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-06-27 12:51+0000\n"
"PO-Revision-Date: 2023-07-16 22:00+0000\n"
"Last-Translator: Freek Engelbarts <freekengelbarts@gmail.com>\n"
"Language-Team: Dutch <https://translate.pretix.eu/projects/pretix/pretix/nl/>"
"\n"
"PO-Revision-Date: 2023-06-07 15:04+0000\n"
"Last-Translator: Thomas Vranken <thvranken@gmail.com>\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"
@@ -40,7 +40,7 @@ msgstr "Chinees (versimpeld)"
#: pretix/_base_settings.py:83
msgid "Chinese (traditional)"
msgstr "Chinees (traditioneel)"
msgstr ""
#: pretix/_base_settings.py:84
msgid "Czech"
@@ -522,16 +522,22 @@ msgid "Test-Mode of shop has been deactivated"
msgstr "Testmode van winkel gedeactiveerd"
#: pretix/api/webhooks.py:339
#, fuzzy
#| msgid "Waiting list entry"
msgid "Waiting list entry added"
msgstr "Wachtlijstitem toegevoegd"
msgstr "Wachtlijstitem"
#: pretix/api/webhooks.py:343
#, fuzzy
#| msgid "Waiting list entry"
msgid "Waiting list entry changed"
msgstr "Wachtlijstitem aangepast"
msgstr "Wachtlijstitem"
#: pretix/api/webhooks.py:347
#, fuzzy
#| msgid "Waiting list entry"
msgid "Waiting list entry deleted"
msgstr "Wachtlijstitem verwijderd"
msgstr "Wachtlijstitem"
#: pretix/api/webhooks.py:351
#, fuzzy
@@ -1673,7 +1679,7 @@ msgstr "Toon alleen met geldig lidmaatschap"
#: pretix/base/exporters/json.py:51 pretix/base/exporters/orderlist.py:85
msgid "Order data"
msgstr "Bestelgegevens"
msgstr "Besteldatums"
#: pretix/base/exporters/json.py:53
msgid ""
@@ -3193,9 +3199,11 @@ msgid "Modern Invoice Renderer (pretix 2.7)"
msgstr "Moderne factuurrenderer (pretix 2.7)"
#: pretix/base/invoice.py:947
#, fuzzy
#| msgid "Please enter a valid state."
msgctxt "invoice"
msgid "(Please quote at all times.)"
msgstr "(Gelieve steeds te vermelden.)"
msgstr "Kies een geldige staat."
#: pretix/base/media.py:58
msgid "Barcode / QR-Code"
@@ -6551,7 +6559,7 @@ msgstr "Productcategorie"
#: pretix/base/pdf.py:151 pretix/base/pdf.py:156
msgid "123.45 EUR"
msgstr "123,45"
msgstr "123,45 EUR"
#: pretix/base/pdf.py:155
msgid "Price including add-ons"
@@ -16061,7 +16069,7 @@ msgstr "Meldingen"
#: pretix/control/navigation.py:390
msgid "2FA"
msgstr "tweefactorauthenticatie"
msgstr "2FA"
#: pretix/control/navigation.py:395
msgid "Authorized apps"
@@ -20392,7 +20400,7 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/order/change.html:250
msgid ""
msgstr "-"
msgstr ""
#: pretix/control/templates/pretixcontrol/order/change.html:274
#, fuzzy
@@ -31039,8 +31047,10 @@ msgstr "Toon volgende week, %(week)s"
#: pretix/presale/templates/pretixpresale/fragment_week_calendar.html:57
#: pretix/presale/templates/pretixpresale/organizers/index.html:85
#: pretix/presale/views/widget.py:376
#, fuzzy
#| msgid "PDF ticket layout"
msgid "Few tickets left"
msgstr "Laatste kaarten"
msgstr "PDF-ticketlay-out"
#: pretix/presale/templates/pretixpresale/event/fragment_subevent_list.html:28
#: pretix/presale/templates/pretixpresale/fragment_calendar.html:75
@@ -31048,9 +31058,11 @@ msgstr "Laatste kaarten"
#: pretix/presale/templates/pretixpresale/fragment_week_calendar.html:60
#: pretix/presale/templates/pretixpresale/organizers/index.html:88
#: pretix/presale/views/widget.py:381
#, fuzzy
#| msgid "Pay now"
msgctxt "available_event_in_list"
msgid "Buy now"
msgstr "Koop nu"
msgstr "Betaal nu"
#: pretix/presale/templates/pretixpresale/event/fragment_subevent_list.html:30
#: pretix/presale/templates/pretixpresale/event/fragment_subevent_list.html:45

View File

@@ -8,16 +8,14 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-06-27 12:51+0000\n"
"PO-Revision-Date: 2023-07-02 06:00+0000\n"
"Last-Translator: Thomas Vranken <thvranken@gmail.com>\n"
"Language-Team: Dutch (Belgium) <https://translate.pretix.eu/projects/pretix/"
"pretix/nl_BE/>\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: nl_BE\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.17\n"
#: pretix/_base_settings.py:78
msgid "English"
@@ -3080,7 +3078,7 @@ msgstr ""
#: pretix/base/invoice.py:947
msgctxt "invoice"
msgid "(Please quote at all times.)"
msgstr "(Gelieve steeds te vermelden.)"
msgstr ""
#: pretix/base/media.py:58
msgid "Barcode / QR-Code"

View File

@@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-06-27 12:51+0000\n"
"PO-Revision-Date: 2023-07-16 22:00+0000\n"
"Last-Translator: Freek Engelbarts <freekengelbarts@gmail.com>\n"
"PO-Revision-Date: 2023-02-23 23:00+0000\n"
"Last-Translator: Toon Toetenel <toon@toetenel.com>\n"
"Language-Team: Dutch (informal) <https://translate.pretix.eu/projects/pretix/"
"pretix/nl_Informal/>\n"
"Language: nl_Informal\n"
@@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.17\n"
"X-Generator: Weblate 4.15.2\n"
#: pretix/_base_settings.py:78
msgid "English"
@@ -41,11 +41,11 @@ msgstr "Chinees (versimpeld)"
#: pretix/_base_settings.py:83
msgid "Chinese (traditional)"
msgstr "Chinees (traditioneel)"
msgstr ""
#: pretix/_base_settings.py:84
msgid "Czech"
msgstr "Tsjechisch"
msgstr ""
#: pretix/_base_settings.py:85
msgid "Danish"
@@ -69,7 +69,7 @@ msgstr "Fins"
#: pretix/_base_settings.py:90
msgid "Galician"
msgstr "Galicisch"
msgstr ""
#: pretix/_base_settings.py:91
msgid "Greek"
@@ -97,7 +97,7 @@ msgstr "Portugees (Brazilië)"
#: pretix/_base_settings.py:97
msgid "Romanian"
msgstr "Roemeens"
msgstr ""
#: pretix/_base_settings.py:98
msgid "Russian"
@@ -113,7 +113,7 @@ msgstr "Turks"
#: pretix/_base_settings.py:101
msgid "Ukrainian"
msgstr "Oekraïens"
msgstr ""
#: pretix/api/auth/devicesecurity.py:31
msgid ""
@@ -762,17 +762,17 @@ msgstr "Antwoorden op vragen"
#: pretix/base/exporters/orderlist.py:1189
#: pretix/plugins/reports/exporters.py:451
#: pretix/plugins/reports/exporters.py:624
#, fuzzy
#| msgid "Order data"
msgctxt "export_category"
msgid "Order data"
msgstr "Bestelgegevens"
msgstr "Besteldatums"
#: pretix/base/exporters/answers.py:54
msgid ""
"Download a ZIP file including all files that have been uploaded by your "
"customers while creating an order."
msgstr ""
"Download een ZIP-bestand dat alle bestanden bevat die bij bestellingen als "
"antwoord op een vraag geüpload werden."
#: pretix/base/exporters/answers.py:64 pretix/base/models/items.py:1565
#: pretix/control/navigation.py:182
@@ -1709,7 +1709,7 @@ msgstr "Verberg zonder geldig lidmaatschap"
#: pretix/base/exporters/json.py:51 pretix/base/exporters/orderlist.py:85
msgid "Order data"
msgstr "Bestelgegevens"
msgstr "Besteldatums"
#: pretix/base/exporters/json.py:53
msgid ""
@@ -2305,8 +2305,10 @@ msgid "Transaction time"
msgstr "Transactiecode"
#: pretix/base/exporters/orderlist.py:833
#, fuzzy
#| msgid "Order data"
msgid "Old data"
msgstr "Oude gegevens"
msgstr "Besteldatums"
#: pretix/base/exporters/orderlist.py:836 pretix/base/models/items.py:1361
#: pretix/control/templates/pretixcontrol/order/transactions.html:22
@@ -3247,9 +3249,11 @@ msgid "Modern Invoice Renderer (pretix 2.7)"
msgstr "Moderne factuurrenderer (pretix 2.7)"
#: pretix/base/invoice.py:947
#, fuzzy
#| msgid "Please enter a valid state."
msgctxt "invoice"
msgid "(Please quote at all times.)"
msgstr "(Gelieve steeds te vermelden.)"
msgstr "Kies een geldige staat."
#: pretix/base/media.py:58
msgid "Barcode / QR-Code"
@@ -15227,8 +15231,10 @@ msgid "The order's internal comment has been updated."
msgstr "Het interne commentaar van de bestelling is bijgewerkt."
#: pretix/control/logdisplay.py:405
#, fuzzy
#| msgid "The order of items has been updated."
msgid "The order's follow-up date has been updated."
msgstr "De opvolgdatum van de bestelling werd bijgewerkt."
msgstr "De volgorde van items is bijgewerkt."
#: pretix/control/logdisplay.py:406
msgid "The order's flag to require attention at check-in has been toggled."

View File

@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-06-27 12:51+0000\n"
"PO-Revision-Date: 2023-07-04 06:00+0000\n"
"PO-Revision-Date: 2023-05-27 22:00+0000\n"
"Last-Translator: Maciej Szymczak <maciej+github@szymczak.at>\n"
"Language-Team: Polish <https://translate.pretix.eu/projects/pretix/pretix/pl/"
">\n"
@@ -42,7 +42,7 @@ msgstr "Chiński (uproszczony)"
#: pretix/_base_settings.py:83
msgid "Chinese (traditional)"
msgstr "Chiński (tradycyjny)"
msgstr ""
#: pretix/_base_settings.py:84
msgid "Czech"
@@ -244,6 +244,8 @@ msgid "Item meta data property '{name}' does not exist."
msgstr "Klucz metadanych '{name}' nie istnieje."
#: pretix/api/serializers/item.py:182 pretix/control/forms/item.py:1082
#, fuzzy
#| msgid "The add-on's category must belong to the same event as the item."
msgid "The bundled item must not be the same item as the bundling one."
msgstr "Dodatek w pakiecie nie może być tym samym co bilet."
@@ -437,6 +439,7 @@ msgid "External refund of payment"
msgstr "Zewnętrzny zwrot płatności"
#: pretix/api/webhooks.py:258
#, fuzzy
msgid "Refund of payment requested by customer"
msgstr "Żądanie zwrotu płatności zgłoszone przez klienta"
@@ -524,20 +527,28 @@ msgid "Test-Mode of shop has been deactivated"
msgstr "Tryb testowy sklepu został wyłączony"
#: pretix/api/webhooks.py:339
#, fuzzy
#| msgid "Waiting list entry"
msgid "Waiting list entry added"
msgstr "Dodano wpis na listę oczekujących"
msgstr "Numer na liście"
#: pretix/api/webhooks.py:343
#, fuzzy
#| msgid "Waiting list entry"
msgid "Waiting list entry changed"
msgstr "Zmieniono wpis na liście oczekujących"
msgstr "Numer na liście"
#: pretix/api/webhooks.py:347
#, fuzzy
#| msgid "Waiting list entry"
msgid "Waiting list entry deleted"
msgstr "Usunięto wpis z listy oczekujących"
msgstr "Numer na liście"
#: pretix/api/webhooks.py:351
#, fuzzy
#| msgid "Waiting list entries"
msgid "Waiting list entry received voucher"
msgstr "Osoba z listy oczekujących dostała voucher"
msgstr "Numery na liście"
#: pretix/base/addressvalidation.py:100 pretix/base/addressvalidation.py:103
#: pretix/base/addressvalidation.py:108 pretix/base/forms/questions.py:938
@@ -553,11 +564,11 @@ msgstr "To pole jest wymagane."
#: pretix/base/addressvalidation.py:213
msgid "Enter a postal code in the format XXX."
msgstr "Wprowadź kod pocztowy w formacie XXX."
msgstr ""
#: pretix/base/addressvalidation.py:222 pretix/base/addressvalidation.py:224
msgid "Enter a postal code in the format XXXX."
msgstr "Wprowadź kod pocztowy w formacie XXXX."
msgstr ""
#: pretix/base/auth.py:143
#, python-brace-format
@@ -626,7 +637,8 @@ msgid "Incompatible SSO provider: \"{error}\"."
msgstr "Niekompatybilny dostawca SSO: \"{error}\"."
#: pretix/base/customersso/oidc.py:109
#, python-brace-format
#, fuzzy, python-brace-format
#| msgid "Presale not started"
msgid "You are not requesting \"{scope}\"."
msgstr "Nie prosisz o \"{scope}\"."
@@ -746,6 +758,8 @@ msgstr "Przesyłanie plików z odpowiedziami na pytania"
#: pretix/base/exporters/orderlist.py:1189
#: pretix/plugins/reports/exporters.py:451
#: pretix/plugins/reports/exporters.py:624
#, fuzzy
#| msgid "Order data"
msgctxt "export_category"
msgid "Order data"
msgstr "Dane zamówienia"
@@ -771,6 +785,8 @@ msgid "Customer accounts"
msgstr "Konta klientów"
#: pretix/base/exporters/customers.py:51
#, fuzzy
#| msgid "Cart positions"
msgctxt "export_category"
msgid "Customer accounts"
msgstr "Rachunki klientów"
@@ -960,6 +976,8 @@ msgid "No"
msgstr "Nie"
#: pretix/base/exporters/dekodi.py:42 pretix/base/exporters/invoices.py:66
#, fuzzy
#| msgid "Invoices"
msgctxt "export_category"
msgid "Invoices"
msgstr "Faktury"
@@ -1670,6 +1688,8 @@ msgstr "Wymagaj ważnego członkostwa"
#: pretix/base/exporters/items.py:94 pretix/base/models/items.py:580
#: pretix/base/models/items.py:1041
#, fuzzy
#| msgid "Team members"
msgid "Hide without a valid membership"
msgstr "Ukryj dla osób bez ważnego członkostwa"
@@ -1739,6 +1759,9 @@ msgid "Only paid orders"
msgstr "Tylko opłacone zamówienia"
#: pretix/base/exporters/orderlist.py:115
#, fuzzy
#| msgctxt "checkin"
#| msgid "Include pending orders"
msgid "Include payment amounts"
msgstr "Uwzględnij kwoty płatności"
@@ -1916,6 +1939,7 @@ msgstr "Kanał sprzedaży"
#: pretix/base/exporters/orderlist.py:279
#: pretix/base/exporters/orderlist.py:583 pretix/base/models/orders.py:233
#: pretix/control/forms/filter.py:238
#, fuzzy
msgid "Follow-up date"
msgstr "Data kontynuacji"
@@ -2285,13 +2309,18 @@ msgstr "Identyfikator zasady podatkowej"
#: pretix/base/exporters/orderlist.py:853
#: pretix/plugins/reports/accountingreport.py:250
#, fuzzy
#| msgctxt "invoice"
#| msgid "Gross value"
msgid "Gross total"
msgstr "Wartość brutto"
#: pretix/base/exporters/orderlist.py:854
#: pretix/plugins/reports/accountingreport.py:249
#, fuzzy
#| msgid "Total"
msgid "Tax total"
msgstr "Razem (podatki)"
msgstr "Razem"
#: pretix/base/exporters/orderlist.py:864
msgid ""
@@ -2574,8 +2603,6 @@ msgstr "Karty podarunkowe"
#: pretix/base/exporters/orderlist.py:1237
msgid "Download a spreadsheet of all gift cards including their current value."
msgstr ""
"Pobierz arkusz kalkulacyjny zawierający wszystkie karty podarunkowe wraz z "
"ich aktualną wartością."
#: pretix/base/exporters/orderlist.py:1244
msgid "Show value at"
@@ -2583,7 +2610,7 @@ msgstr "Pokaż wartość w"
#: pretix/base/exporters/orderlist.py:1247
msgid "Defaults to the time of report."
msgstr "Domyślny czas raportu."
msgstr ""
#: pretix/base/exporters/orderlist.py:1252
#: pretix/base/exporters/orderlist.py:1262 pretix/control/forms/filter.py:507
@@ -2605,6 +2632,7 @@ msgid "All"
msgstr "Zaznacz wszystko"
#: pretix/base/exporters/orderlist.py:1254 pretix/control/forms/filter.py:1315
#, fuzzy
msgid "Live"
msgstr "Na żywo"
@@ -2614,10 +2642,12 @@ msgid "Empty"
msgstr "Pusty"
#: pretix/base/exporters/orderlist.py:1264 pretix/control/forms/filter.py:1324
#, fuzzy
msgid "Valid and with value"
msgstr "Ważny z wartością"
#: pretix/base/exporters/orderlist.py:1265 pretix/control/forms/filter.py:1325
#, fuzzy
msgid "Expired and with value"
msgstr "Wygaśnięty z wartością"
@@ -2655,25 +2685,33 @@ msgid "Current value"
msgstr "Wartość netto"
#: pretix/base/exporters/orderlist.py:1310
#, fuzzy
#| msgid "Creation date"
msgid "Created in order"
msgstr "Data stworzenia"
#: pretix/base/exporters/orderlist.py:1311
#, fuzzy
#| msgid "Invoice number"
msgid "Last invoice number of order"
msgstr "Numer faktury ostatniego zamówienia"
#: pretix/base/exporters/orderlist.py:1312
#, fuzzy
#| msgid "Expiration date"
msgid "Last invoice date of order"
msgstr "Data ostatniej faktury zamówienia"
msgstr "Data wygaśnięcia"
#: pretix/base/exporters/waitinglist.py:42
#, fuzzy
#| msgid "Waiting list"
msgctxt "export_category"
msgid "Waiting list"
msgstr "Lista oczekiwania"
#: pretix/base/exporters/waitinglist.py:43
msgid "Download a spread sheet with all your waiting list data."
msgstr "Pobierz arkusz kalkulacyjny ze wszystkimi danymi z listy oczekujących."
msgstr ""
#: pretix/base/exporters/waitinglist.py:49
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:87
@@ -2695,7 +2733,7 @@ msgstr "Voucher został przypisany"
#: pretix/base/exporters/waitinglist.py:64
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:95
msgid "Waiting for redemption"
msgstr "Oczekiwanie na wykupienie vouchera"
msgstr ""
#: pretix/base/exporters/waitinglist.py:72
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:208
@@ -2707,6 +2745,7 @@ msgstr "Voucher został wykorzystany"
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:101
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:210
#: pretix/control/views/waitinglist.py:322
#, fuzzy
msgid "Voucher expired"
msgstr "Voucher wygaśnięty"
@@ -2745,8 +2784,6 @@ msgid ""
"Due to technical reasons you cannot set inputs, that need to be masked (e.g. "
"passwords), to %(value)s."
msgstr ""
"Ze względów technicznych nie można ustawić pól, które mają być maskowane ("
"np. hasła) do %(value)s."
#: pretix/base/forms/auth.py:57 pretix/base/forms/auth.py:168
msgid "Keep me logged in"
@@ -2796,9 +2833,11 @@ msgid "Please enter a shorter name."
msgstr "Wpisz proszę krótszą nazwę."
#: pretix/base/forms/questions.py:283
#, fuzzy
#| msgid "Internal reference"
msgctxt "phonenumber"
msgid "International area code"
msgstr "Wewnętrzny kod rejonu"
msgstr "Wewnętrzna adnotacja"
#: pretix/base/forms/questions.py:307
msgctxt "phonenumber"
@@ -2812,9 +2851,10 @@ msgid ""
msgstr "Wgrałeś poziomie zdjęcie. Wymagane jest zdjęcie w pionie."
#: pretix/base/forms/questions.py:471
#, fuzzy
msgid "Please upload an image where the width is 3/4 of the height."
msgstr ""
"Wgraj obrazek, którego proporcje to: szerokość nie mniej niż 3/4 wysokości."
"Wgraj obrazek, którego proporcje to: szerokość nie mniej niż 3/4 wysokości"
#: pretix/base/forms/questions.py:474
msgid ""
@@ -2836,7 +2876,7 @@ msgstr ""
msgid ""
"If you keep this empty, the ticket will be valid starting at the time of "
"purchase."
msgstr "Jeśli to pole pozostanie puste, bilet będzie ważny od momentu zakupu."
msgstr ""
#: pretix/base/forms/questions.py:664 pretix/base/forms/questions.py:992
msgid "Street and Number"
@@ -2852,14 +2892,10 @@ msgid ""
"Optional, but depending on the country you reside in we might need to charge "
"you additional taxes if you do not enter it."
msgstr ""
"Opcjonalne, ale w zależności od kraju, w którym mieszkasz, możemy być "
"zmuszeni do naliczenia dodatkowych podatków, jeśli go nie podasz."
#: pretix/base/forms/questions.py:1033 pretix/base/forms/questions.py:1039
msgid "If you are registered in Switzerland, you can enter your UID instead."
msgstr ""
"Jeśli jesteś zarejestrowany w Szwajcarii, możesz zamiast tego wprowadzić "
"swój identyfikator UID."
#: pretix/base/forms/questions.py:1037
msgid ""

View File

@@ -34,27 +34,24 @@
import json
import logging
import os
import subprocess
import tempfile
from collections import OrderedDict
from datetime import date, datetime, time, timedelta
from datetime import datetime, time, timedelta
from decimal import Decimal
from io import BytesIO
from typing import BinaryIO, List, Optional, Tuple
from typing import Tuple
import dateutil.parser
from django import forms
from django.conf import settings
from django.contrib.staticfiles import finders
from django.core.files import File
from django.core.files.base import ContentFile
from django.core.files.storage import default_storage
from django.db import DataError, models
from django.db.models import Case, Exists, OuterRef, Q, Subquery, When
from django.db.models.functions import Cast, Coalesce
from django.utils.timezone import make_aware
from django.utils.translation import gettext as _, gettext_lazy, pgettext_lazy
from pypdf import PageObject, PdfReader, PdfWriter, Transformation
from pypdf import PdfReader, PdfWriter, Transformation
from pypdf.generic import RectangleObject
from reportlab.lib import pagesizes
from reportlab.lib.units import mm
@@ -62,10 +59,8 @@ from reportlab.pdfgen import canvas
from pretix.base.exporter import BaseExporter
from pretix.base.i18n import language
from pretix.base.models import (
Event, Order, OrderPosition, Question, QuestionAnswer,
)
from pretix.base.pdf import Renderer, merge_background
from pretix.base.models import Order, OrderPosition, Question, QuestionAnswer
from pretix.base.pdf import Renderer
from pretix.base.services.export import ExportError
from pretix.base.settings import PERSON_NAME_SCHEMES
from pretix.helpers.templatetags.jsonfield import JSONExtract
@@ -184,137 +179,9 @@ OPTIONS = OrderedDict([
])
def _chunks(lst, n):
"""
Yield successive n-sized chunks from lst.
"""
for i in range(0, len(lst), n):
yield lst[i:i + n]
def render_pdf(event, positions, opt):
Renderer._register_fonts()
def _render_nup_page(nup_pdf: PdfWriter, input_pages: PageObject, opt: dict) -> PageObject:
"""
Render the `Page` objects in `input_pages` onto one page of `nup_pdf` using the options given in `opt` and
return the newly created page.
"""
badges_per_page = opt['cols'] * opt['rows']
nup_page = nup_pdf.add_blank_page(
width=Decimal('%.5f' % (opt['pagesize'][0])),
height=Decimal('%.5f' % (opt['pagesize'][1])),
)
for i, page in enumerate(input_pages):
di = i % badges_per_page
tx = opt['margins'][3] + (di % opt['cols']) * opt['offsets'][0]
ty = opt['margins'][2] + (opt['rows'] - 1 - (di // opt['cols'])) * opt['offsets'][1]
page.add_transformation(Transformation().translate(tx, ty))
page.mediabox = RectangleObject((
Decimal('%.5f' % (page.mediabox.left.as_numeric() + tx)),
Decimal('%.5f' % (page.mediabox.bottom.as_numeric() + ty)),
Decimal('%.5f' % (page.mediabox.right.as_numeric() + tx)),
Decimal('%.5f' % (page.mediabox.top.as_numeric() + ty))
))
page.trimbox = page.mediabox
nup_page.merge_page(page)
return nup_page
def _merge_pages(file_paths: List[str], output_file: BinaryIO):
"""
Merge all pages from the PDF files named `file_paths` into the `output_file`.
"""
if settings.PDFTK:
subprocess.run([
settings.PDFTK,
*file_paths,
'cat',
'output',
'-',
'compress'
], check=True, stdout=output_file)
else:
merger = PdfWriter()
merger.add_metadata({
'/Title': 'Badges',
'/Creator': 'pretix',
})
# append all temp-PDFs
for pdf in file_paths:
merger.append(pdf)
# write merged PDFs to buffer
merger.write(output_file)
def _render_nup(input_files: List[str], num_pages: int, output_file: BytesIO, opt: dict):
"""
Render the pages from the PDF files listed in `input_files` (file names) with a total number of `num_pages` pages
into one file written to `output_file` using the -nup options given in `opt`.
"""
badges_per_page = opt['cols'] * opt['rows']
max_nup_pages = 20 # chunk size to prevent working with huge files
nup_pdf_files = []
temp_dir = None
if num_pages > badges_per_page * max_nup_pages:
# to reduce memory consumption with lots of badges
# we try to use temporary PDF-files with up to
# max_nup_pages pages
# If temp-files fail, we try to merge in-memory anyways
try:
temp_dir = tempfile.TemporaryDirectory()
except IOError:
pass
try:
badges_pdf = PdfReader(input_files.pop())
offset = 0
for i, chunk_indices in enumerate(_chunks(range(num_pages), badges_per_page * max_nup_pages)):
chunk = []
for j in chunk_indices:
# We need to dynamically switch to the next input file as we don't know how many pages each input
# file has beforehand
if j - offset >= len(badges_pdf.pages):
offset += len(badges_pdf.pages)
badges_pdf = PdfReader(input_files.pop())
chunk.append(badges_pdf.pages[j - offset])
# Reset some internal state from pypdf. This will make it a little slower, but will prevent us from
# running out of memory if we process a really large file.
badges_pdf.flattened_pages = None
nup_pdf = PdfWriter()
nup_pdf.add_metadata({
'/Title': 'Badges',
'/Creator': 'pretix',
})
for page_chunk in _chunks(chunk, badges_per_page):
_render_nup_page(nup_pdf, page_chunk, opt)
if temp_dir:
file_path = os.path.join(temp_dir.name, 'badges-%d.pdf' % i)
nup_pdf.write(file_path)
nup_pdf_files.append(file_path)
else:
# everything fitted into one nup_pdf -- we can save some work
nup_pdf.write(output_file)
return
del badges_pdf # free up memory
file_paths = [os.path.join(temp_dir.name, fp) for fp in nup_pdf_files]
_merge_pages(file_paths, output_file)
finally:
if temp_dir:
try:
temp_dir.cleanup()
except IOError:
pass
def _render_badges(event: Event, positions: List[OrderPosition], opt: dict) -> Tuple[PdfWriter, PdfWriter, int]:
"""
Render the badges for the given order positions into two different files, one with the foregrounds and one with
the backgrounds.
"""
renderermap = {
bi.item_id: _renderer(event, bi.layout)
for bi in BadgeItem.objects.select_related('layout').filter(item__event=event)
@@ -328,13 +195,12 @@ def _render_badges(event: Event, positions: List[OrderPosition], opt: dict) -> T
if not len(op_renderers):
raise ExportError(_("None of the selected products is configured to print badges."))
fg_pdf = PdfWriter()
fg_pdf.add_metadata({
# render each badge on its own page first
merger = PdfWriter()
merger.add_metadata({
'/Title': 'Badges',
'/Creator': 'pretix',
})
bg_pdf = PdfWriter()
num_pages = 0
for op, renderer in op_renderers:
buffer = BytesIO()
page = canvas.Canvas(buffer, pagesize=pagesizes.A4)
@@ -344,52 +210,46 @@ def _render_badges(event: Event, positions: List[OrderPosition], opt: dict) -> T
if opt['pagesize']:
page.setPageSize(opt['pagesize'])
page.save()
# to reduce disk-IO render backgrounds in own PDF and merge later
fg_pdf.append(buffer)
new_num_pages = len(fg_pdf.pages)
for i in range(new_num_pages - num_pages):
bg_pdf.add_page(renderer.bg_pdf.pages[i])
num_pages = new_num_pages
buffer = renderer.render_background(buffer, _('Badge'))
merger.append(ContentFile(buffer.read()))
return fg_pdf, bg_pdf, num_pages
outbuffer = BytesIO()
merger.write(outbuffer)
outbuffer.seek(0)
def render_pdf(event, positions, opt, output_file):
Renderer._register_fonts()
badges_per_page = opt['cols'] * opt['rows']
if badges_per_page == 1:
fg_pdf, bg_pdf, _ = _render_badges(event, positions, opt)
merge_background(
fg_pdf,
bg_pdf,
output_file,
compress=True,
)
else:
# place n-up badges/pages per page
with tempfile.TemporaryDirectory() as tmp_dir:
page_pdfs = []
total_num_pages = 0
for position_chunk in _chunks(positions, 200):
# We first render the foreground and background of every individual badge and merge them, but we do
# so in chunks, since the n-up code is slower if it has to deal with huge PDFs. It doesn't matter
# that not every position has the same number of pages, as the n-up code can deal with that
fg_pdf, bg_pdf, num_pages = _render_badges(event, position_chunk, opt)
out_pdf_name = os.path.join(tmp_dir, f'chunk-{len(page_pdfs)}.pdf')
with open(out_pdf_name, 'wb') as out_pdf:
merge_background(
fg_pdf,
bg_pdf,
out_pdf,
compress=False,
)
page_pdfs.append(out_pdf_name)
total_num_pages += num_pages
del fg_pdf, bg_pdf # free up memory
# no need to place multiple badges on one page
return outbuffer
# Actually render a n-up file
return _render_nup(page_pdfs, total_num_pages, output_file, opt)
# place n-up badges/pages per page
badges_pdf = PdfReader(outbuffer)
nup_pdf = PdfWriter()
nup_page = None
for i, page in enumerate(badges_pdf.pages):
di = i % badges_per_page
if di == 0:
nup_page = nup_pdf.add_blank_page(
width=Decimal('%.5f' % (opt['pagesize'][0])),
height=Decimal('%.5f' % (opt['pagesize'][1])),
)
tx = opt['margins'][3] + (di % opt['cols']) * opt['offsets'][0]
ty = opt['margins'][2] + (opt['rows'] - 1 - (di // opt['cols'])) * opt['offsets'][1]
page.add_transformation(Transformation().translate(tx, ty))
page.mediabox = RectangleObject((
Decimal('%.5f' % (page.mediabox.left.as_numeric() + tx)),
Decimal('%.5f' % (page.mediabox.bottom.as_numeric() + ty)),
Decimal('%.5f' % (page.mediabox.right.as_numeric() + tx)),
Decimal('%.5f' % (page.mediabox.top.as_numeric() + ty))
))
page.trimbox = page.mediabox
nup_page.merge_page(page)
outbuffer = BytesIO()
nup_pdf.write(outbuffer)
outbuffer.seek(0)
return outbuffer
class BadgeExporter(BaseExporter):
@@ -475,7 +335,7 @@ class BadgeExporter(BaseExporter):
)
return d
def render(self, form_data: dict, output_file=None) -> Tuple[str, str, Optional[bytes]]:
def render(self, form_data: dict) -> Tuple[str, str, str]:
qs = OrderPosition.objects.filter(
order__event=self.event, item_id__in=form_data['items']
).prefetch_related(
@@ -491,19 +351,15 @@ class BadgeExporter(BaseExporter):
qs = qs.filter(Q(order__status=Order.STATUS_PAID) | Q(order__status=Order.STATUS_PENDING, order__valid_if_pending=True))
if form_data.get('date_from'):
if not isinstance(form_data.get('date_from'), date):
form_data['date_from'] = dateutil.parser.parse(form_data['date_from']).date()
df = make_aware(datetime.combine(
form_data['date_from'],
dt = make_aware(datetime.combine(
dateutil.parser.parse(form_data['date_from']).date(),
time(hour=0, minute=0, second=0)
), self.event.timezone)
qs = qs.filter(Q(subevent__date_from__gte=df) | Q(subevent__isnull=True, order__event__date_from__gte=df))
qs = qs.filter(Q(subevent__date_from__gte=dt) | Q(subevent__isnull=True, order__event__date_from__gte=dt))
if form_data.get('date_to'):
if not isinstance(form_data.get('date_to'), date):
form_data['date_to'] = dateutil.parser.parse(form_data['date_to']).date()
dt = make_aware(datetime.combine(
form_data['date_to'] + timedelta(days=1),
dateutil.parser.parse(form_data['date_to']).date() + timedelta(days=1),
time(hour=0, minute=0, second=0)
), self.event.timezone)
qs = qs.filter(Q(subevent__date_from__lt=dt) | Q(subevent__isnull=True, order__event__date_from__lt=dt))
@@ -571,17 +427,11 @@ class BadgeExporter(BaseExporter):
)
try:
if output_file:
render_pdf(self.event, qs, OPTIONS[form_data.get('rendering', 'one')], output_file=output_file)
return 'badges.pdf', 'application/pdf', None
else:
with tempfile.NamedTemporaryFile(delete=True) as tmpfile:
render_pdf(self.event, qs, OPTIONS[form_data.get('rendering', 'one')], output_file=tmpfile)
tmpfile.seek(0)
return 'badges.pdf', 'application/pdf', tmpfile.read()
outbuffer = render_pdf(self.event, qs, OPTIONS[form_data.get('rendering', 'one')])
except DataError:
logging.exception('DataError during export')
raise ExportError(
_('Your data could not be converted as requested. This could be caused by invalid values in your '
'databases, such as answers to number questions which are not a number.')
)
return 'badges.pdf', 'application/pdf', outbuffer.read()

View File

@@ -20,7 +20,6 @@
# <https://www.gnu.org/licenses/>.
#
import logging
import tempfile
from typing import List
from django.core.files.base import ContentFile
@@ -42,10 +41,7 @@ logger = logging.getLogger(__name__)
def badges_create_pdf(event: Event, fileid: int, positions: List[int]) -> int:
file = CachedFile.objects.get(id=fileid)
with tempfile.TemporaryFile() as tmp_file:
render_pdf(event, OrderPosition.objects.filter(id__in=positions), opt=OPTIONS['one'],
output_file=tmp_file)
tmp_file.seek(0)
file.file.save(cachedfile_name(file, file.filename), ContentFile(tmp_file.read()))
file.save()
pdfcontent = render_pdf(event, OrderPosition.objects.filter(id__in=positions), opt=OPTIONS['one'])
file.file.save(cachedfile_name(file, file.filename), ContentFile(pdfcontent.read()))
file.save()
return file.pk

View File

@@ -49,7 +49,7 @@ class RuleSerializer(I18nAwareModelSerializer):
if not full_data.get('send_date'):
raise ValidationError('send_date is required for date_is_absolute=True')
else:
if not all([full_data.get(k) is not None for k in ['send_offset_days', 'send_offset_time']]):
if not all([full_data.get(k) for k in ['send_offset_days', 'send_offset_time']]):
raise ValidationError('send_offset_days and send_offset_time are required for date_is_absolute=False')
if full_data.get('all_products') is False:

View File

@@ -24,22 +24,18 @@ from collections import OrderedDict
from django import forms
from django.dispatch import receiver
from django.http import HttpRequest
from django.template.loader import get_template
from django.urls import resolve, reverse
from django.utils.translation import gettext_lazy as _
from paypalhttp import HttpResponse
from pretix.base.forms import SecretKeySettingsField
from pretix.base.middleware import _merge_csp, _parse_csp, _render_csp
from pretix.base.settings import settings_hierarkey
from pretix.base.signals import (
logentry_display, register_global_settings, register_payment_providers,
)
from pretix.control.signals import nav_organizer
from pretix.plugins.stripe.forms import StripeKeyValidator
from pretix.plugins.stripe.payment import StripeMethod
from pretix.presale.signals import html_head, process_response
from pretix.presale.signals import html_head
@receiver(register_payment_providers, dispatch_uid="payment_stripe")
@@ -182,34 +178,3 @@ def nav_o(sender, request, organizer, **kwargs):
'active': 'settings.connect' in url.url_name,
}]
return []
@receiver(signal=process_response, dispatch_uid="stripe_middleware_resp")
def signal_process_response(sender, request: HttpRequest, response: HttpResponse, **kwargs):
provider = StripeMethod(sender)
url = resolve(request.path_info)
if provider.settings.get('_enabled', as_type=bool) and (
url.url_name == "event.order.pay.change" or
url.url_name == "event.order.pay" or
(url.url_name == "event.checkout" and url.kwargs['step'] == "payment") or
(url.namespace == "plugins:stripe" and url.url_name in ["sca", "sca.return"])
):
if 'Content-Security-Policy' in response:
h = _parse_csp(response['Content-Security-Policy'])
else:
h = {}
# https://stripe.com/docs/security/guide#content-security-policy
csps = {
'connect-src': ['https://api.stripe.com'],
'frame-src': ['https://js.stripe.com', 'https://hooks.stripe.com'],
'script-src': ['https://js.stripe.com'],
}
_merge_csp(h, csps)
if h:
response['Content-Security-Policy'] = _render_csp(h)
return response

View File

@@ -26,10 +26,8 @@
{% if payment_info.source.type == "bancontact" %}
<dt>{% trans "Bank" %}</dt>
<dd>{{ payment_info.source.bancontact.bank_name }} ({{ payment_info.source.bancontact.bic }})</dd>
{% if owner in payment_info.source %}
<dt>{% trans "Payer name" %}</dt>
<dd>{{ payment_info.source.owner.verified_name|default:payment_info.source.owner.name }}</dd>
{% endif %}
<dt>{% trans "Payer name" %}</dt>
<dd>{{ payment_info.source.owner.verified_name|default:payment_info.source.owner.name }}</dd>
{% endif %}
{% if payment_info.source.type == "ideal" %}
<dt>{% trans "Bank" %}</dt>

View File

@@ -52,7 +52,6 @@ from pretix.base.customersso.oidc import (
from pretix.base.models import Customer, InvoiceAddress, Order, OrderPosition
from pretix.base.services.mail import mail
from pretix.base.settings import PERSON_NAME_SCHEMES
from pretix.base.signals import customer_created, customer_signed_in
from pretix.helpers.compat import CompatDeleteView
from pretix.helpers.http import redirect_to_url
from pretix.multidomain.models import KnownDomain
@@ -152,9 +151,7 @@ class LoginView(RedirectBackMixin, FormView):
def form_valid(self, form):
"""Security check complete. Log the user in."""
customer = form.get_customer()
customer_login(self.request, customer)
customer_signed_in.send(customer.organizer, customer=customer)
customer_login(self.request, form.get_customer())
return HttpResponseRedirect(self.get_success_url())
@@ -240,8 +237,7 @@ class RegistrationView(RedirectBackMixin, FormView):
def form_valid(self, form):
with transaction.atomic():
customer = form.create()
customer_created.send(customer.organizer, customer=customer)
form.create()
messages.success(
self.request,
_('Your account has been created. Please follow the link in the email we sent you to activate your '
@@ -760,7 +756,6 @@ class SSOLoginReturnView(RedirectBackMixin, View):
)
try:
customer.save(force_insert=True)
customer_created.send(customer.organizer, customer=customer)
except IntegrityError:
# This might either be a race condition or the email address is taken
# by a different customer account
@@ -824,7 +819,6 @@ class SSOLoginReturnView(RedirectBackMixin, View):
})
else:
customer_login(self.request, customer)
customer_signed_in.send(customer.organizer, customer=customer)
return redirect_to_url(self.get_success_url(redirect_to))
def _fail(self, message, popup_origin):

View File

@@ -116,29 +116,6 @@ elif 'mysql' in db_backend:
db_options = {}
postgresql_sslmode = config.get('database', 'sslmode', fallback='disable')
USE_DATABASE_TLS = postgresql_sslmode != 'disable'
USE_DATABASE_MTLS = USE_DATABASE_TLS and config.has_option('database', 'sslcert')
if USE_DATABASE_TLS or USE_DATABASE_MTLS:
tls_config = {}
if not USE_DATABASE_MTLS:
if 'postgresql' in db_backend:
tls_config = {
'sslmode': config.get('database', 'sslmode'),
'sslrootcert': config.get('database', 'sslrootcert'),
}
else:
if 'postgresql' in db_backend:
tls_config = {
'sslmode': config.get('database', 'sslmode'),
'sslrootcert': config.get('database', 'sslrootcert'),
'sslcert': config.get('database', 'sslcert'),
'sslkey': config.get('database', 'sslkey'),
}
db_options.update(tls_config)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.' + db_backend,
@@ -251,9 +228,6 @@ if HAS_MEMCACHED:
HAS_REDIS = config.has_option('redis', 'location')
USE_REDIS_SENTINEL = config.has_option('redis', 'sentinels')
redis_ssl_cert_reqs = config.get('redis', 'ssl_cert_reqs', fallback='none')
USE_REDIS_TLS = redis_ssl_cert_reqs != 'none'
USE_REDIS_MTLS = USE_REDIS_TLS and config.has_option('redis', 'ssl_certfile')
HAS_REDIS_PASSWORD = config.has_option('redis', 'password')
if HAS_REDIS:
OPTIONS = {
@@ -269,29 +243,6 @@ if HAS_REDIS:
OPTIONS["SENTINEL_KWARGS"] = {"socket_timeout": 1}
OPTIONS["SENTINELS"] = [tuple(sentinel) for sentinel in loads(config.get('redis', 'sentinels'))]
if USE_REDIS_TLS or USE_REDIS_MTLS:
tls_config = {}
if not USE_REDIS_MTLS:
tls_config = {
'ssl_cert_reqs': config.get('redis', 'ssl_cert_reqs'),
'ssl_ca_certs': config.get('redis', 'ssl_ca_certs'),
}
else:
tls_config = {
'ssl_cert_reqs': config.get('redis', 'ssl_cert_reqs'),
'ssl_ca_certs': config.get('redis', 'ssl_ca_certs'),
'ssl_keyfile': config.get('redis', 'ssl_keyfile'),
'ssl_certfile': config.get('redis', 'ssl_certfile'),
}
if USE_REDIS_SENTINEL is False:
# The CONNECTION_POOL_KWARGS option is necessary for self-signed certs. For further details, please check
# https://github.com/jazzband/django-redis/issues/554#issuecomment-949498321
OPTIONS["CONNECTION_POOL_KWARGS"] = tls_config
OPTIONS["REDIS_CLIENT_KWARGS"].update(tls_config)
else:
OPTIONS["SENTINEL_KWARGS"].update(tls_config)
if HAS_REDIS_PASSWORD:
OPTIONS["PASSWORD"] = config.get('redis', 'password')

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,31 +1,6 @@
/*global $ */
setup_collapsible_details = function (el) {
el.find('details.sneak-peek:not([open])').each(function() {
this.open = true;
var $elements = $("> :not(summary)", this).show().filter(':not(.sneak-peek-trigger)').attr('aria-hidden', 'true');
var container = this;
var trigger = $('summary, .sneak-peek-trigger button', container);
function onclick(e) {
e.preventDefault();
container.addEventListener('transitionend', function() {
$(container).removeClass('sneak-peek');
container.style.removeProperty('height');
}, {once: true});
container.style.height = container.scrollHeight + 'px';
$('.sneak-peek-trigger', container).fadeOut(function() {
$(this).remove();
});
$elements.removeAttr('aria-hidden');
trigger.off('click', onclick);
}
trigger.on('click', onclick);
});
var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]';
el.find("details summary").click(function (e) {
if (this.tagName !== "A" && $(e.target).closest("a").length > 0) {

View File

@@ -123,8 +123,6 @@ var form_handlers = function (el) {
// Vouchers
el.find("#voucher-bulk-codes-generate").click(function () {
if (!$("#voucher-bulk-codes-num").get(0).reportValidity())
return;
var num = $("#voucher-bulk-codes-num").val();
var prefix = $('#voucher-bulk-codes-prefix').val();
if (num != "") {

View File

@@ -307,6 +307,30 @@ $(function () {
$("#ajaxerr").on("click", ".ajaxerr-close", ajaxErrDialog.hide);
$('details.sneak-peek:not([open])').each(function() {
this.open = true;
var $elements = $("> :not(summary)", this).show().filter(':not(.sneak-peek-trigger)').attr('aria-hidden', 'true');
var container = this;
var trigger = $('summary, .sneak-peek-trigger button', container);
function onclick(e) {
e.preventDefault();
container.addEventListener('transitionend', function() {
$(container).removeClass('sneak-peek');
container.style.removeProperty('height');
}, {once: true});
container.style.height = container.scrollHeight + 'px';
$('.sneak-peek-trigger', container).fadeOut(function() {
$(this).remove();
});
$elements.removeAttr('aria-hidden');
trigger.off('click', onclick);
}
trigger.on('click', onclick);
});
// Copy answers
$(".js-copy-answers").click(function (e) {
e.preventDefault();

View File

@@ -126,7 +126,7 @@ def test_event_validate(token_client, organizer, team, event):
assert resp.data == {"_format": ["\"FOOBAR\" is not a valid choice."]}
@pytest.mark.django_db(transaction=True)
@pytest.mark.django_db
def test_org_validate_events(token_client, organizer, team, event):
resp = token_client.post('/api/v1/organizers/{}/exporters/orderlist/run/'.format(organizer.slug), data={
'_format': 'xlsx',
@@ -164,7 +164,7 @@ def test_org_validate_events(token_client, organizer, team, event):
assert resp.data == {"events": [f"Object with slug={event.slug} does not exist."]}
@pytest.mark.django_db(transaction=True)
@pytest.mark.django_db
def test_org_run_limit_events(token_client, organizer, team, event, event2):
resp = token_client.post('/api/v1/organizers/{}/exporters/eventdata/run/'.format(organizer.slug), data={
'_format': 'default',
@@ -199,7 +199,7 @@ def test_org_run_limit_events(token_client, organizer, team, event, event2):
assert resp.getvalue().strip().count(b"\n") == 1
@pytest.mark.django_db(transaction=True)
@pytest.mark.django_db
def test_run_success(token_client, organizer, team, event):
resp = token_client.post('/api/v1/organizers/{}/events/{}/exporters/orderlist/run/'.format(organizer.slug, event.slug), data={
'_format': 'xlsx',
@@ -212,7 +212,7 @@ def test_run_success(token_client, organizer, team, event):
assert resp["Content-Type"] == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
@pytest.mark.django_db(transaction=True)
@pytest.mark.django_db
def test_run_success_old_date_frame(token_client, organizer, team, event):
resp = token_client.post('/api/v1/organizers/{}/events/{}/exporters/orderlist/run/'.format(organizer.slug, event.slug), data={
'_format': 'xlsx',
@@ -261,7 +261,7 @@ def test_gone_without_celery(token_client, organizer, team, event):
assert resp.status_code == 410
@pytest.mark.django_db(transaction=True)
@pytest.mark.django_db
def test_org_level_export(token_client, organizer, team, event):
resp = token_client.post('/api/v1/organizers/{}/exporters/giftcardlist/run/'.format(organizer.slug), data={
'date': '2022-10-05T00:00:00Z',

View File

@@ -119,23 +119,6 @@ def test_sendmail_rule_create_min_fail(token_client, organizer, event):
)
@scopes_disabled()
@pytest.mark.django_db
def test_sendmail_rule_offset_zero(token_client, organizer, event):
create_rule(
token_client, organizer, event,
data={
'subject': {'en': 'meow'},
'template': {'en': 'creative text here'},
'send_date': '2018-03-17T13:31Z',
'send_offset_days': '0',
'send_offset_time': '08:40',
'date_is_absolute': False,
},
expected_failure=False,
)
@scopes_disabled()
@pytest.mark.django_db
def test_sendmail_rule_create_minimal(token_client, organizer, event):

View File

@@ -156,7 +156,7 @@ def test_event_fail_user_no_permission(event, user, team):
assert djmail.outbox[0].to == [user.email]
@pytest.mark.django_db(transaction=True)
@pytest.mark.django_db
@freeze_time("2023-01-18 03:00:00+01:00")
def test_event_ok(event, user, team):
djmail.outbox = []
@@ -286,7 +286,7 @@ def test_organizer_fail_user_does_not_have_specific_permission(event, user, team
assert djmail.outbox[0].to == [user.email]
@pytest.mark.django_db(transaction=True)
@pytest.mark.django_db
@freeze_time("2023-01-18 03:00:00+01:00")
def test_organizer_limited_to_events(event, user, team):
djmail.outbox = []
@@ -323,7 +323,7 @@ def test_organizer_limited_to_events(event, user, team):
assert len(djmail.outbox[0].attachments[0][1].splitlines()) == 2
@pytest.mark.django_db(transaction=True)
@pytest.mark.django_db
@freeze_time("2023-01-18 03:00:00+01:00")
def test_organizer_ok(event, user, team):
djmail.outbox = []

View File

@@ -633,7 +633,7 @@ class EventsTest(SoupTest):
},
follow=True
)
assert doc.select('.alert-danger')
assert doc.select('.alert-warning')
self.event1.settings.flush()
# not yet saved
assert "mail_from" not in self.event1.settings._cache()

View File

@@ -52,7 +52,7 @@ def env():
return event, user, t
@pytest.mark.django_db(transaction=True)
@pytest.mark.django_db
def test_event_export(client, env):
client.login(email="dummy@dummy.dummy", password="dummy")
response = client.get("/control/event/dummy/dummy/orders/export/?identifier=itemdata")
@@ -69,7 +69,7 @@ def test_event_export(client, env):
assert len(b"".join(response.streaming_content).split(b"\n")) == 3
@pytest.mark.django_db(transaction=True)
@pytest.mark.django_db
def test_event_export_schedule(client, env):
client.login(email="dummy@dummy.dummy", password="dummy")
response = client.get("/control/event/dummy/dummy/orders/export/?identifier=itemdata")
@@ -161,7 +161,7 @@ def test_event_export_schedule(client, env):
assert env[0].scheduled_exports.count() == 0
@pytest.mark.django_db(transaction=True)
@pytest.mark.django_db
def test_event_limited_permission(client, env):
env[2].can_change_event_settings = False
env[2].save()
@@ -212,7 +212,7 @@ def test_event_limited_permission(client, env):
assert response.status_code == 302
@pytest.mark.django_db(transaction=True)
@pytest.mark.django_db
def test_organizer_export(client, env):
client.login(email="dummy@dummy.dummy", password="dummy")
response = client.get("/control/organizer/dummy/export/?identifier=eventdata")
@@ -230,7 +230,7 @@ def test_organizer_export(client, env):
assert len(b"".join(response.streaming_content).split(b"\n")) == 3
@pytest.mark.django_db(transaction=True)
@pytest.mark.django_db
def test_organizer_export_schedule(client, env):
client.login(email="dummy@dummy.dummy", password="dummy")
response = client.get("/control/organizer/dummy/export/?identifier=eventdata")
@@ -312,6 +312,7 @@ def test_organizer_export_schedule(client, env):
"schedule-mail_subject": "Product data, my friend!",
"schedule-mail_template": "Mail body"
}, follow=True)
print(response.content)
assert b"Your export schedule has been saved, but no next export is planned" in response.content
s.refresh_from_db()
assert s.schedule_next_run is None
@@ -328,7 +329,7 @@ def test_organizer_export_schedule(client, env):
assert env[0].organizer.scheduled_exports.count() == 0
@pytest.mark.django_db(transaction=True)
@pytest.mark.django_db
def test_organizer_limited_permission(client, env):
env[2].can_change_organizer_settings = False
env[2].save()

View File

@@ -221,7 +221,7 @@ class OrganizerTest(SoupTest):
},
follow=True
)
assert doc.select('.alert-danger')
assert doc.select('.alert-warning')
self.orga1.settings.flush()
# not yet saved
assert "mail_from" not in self.orga1.settings._cache()

View File

@@ -95,12 +95,8 @@ def test_native_disabled(env, client):
@pytest.mark.django_db
def test_org_register(env, client, mocker):
from pretix.base.signals import customer_created
mocker.patch('pretix.base.signals.customer_created.send')
def test_org_register(env, client):
signer = signing.TimestampSigner(salt='customer-registration-captcha-127.0.0.1')
r = client.post('/bigevents/account/register', {
'email': 'john@example.org',
'name_parts_0': 'John Doe',
@@ -113,7 +109,6 @@ def test_org_register(env, client, mocker):
customer = env[0].customers.get(email='john@example.org')
assert not customer.is_verified
assert customer.is_active
customer_created.send.assert_called_once_with(customer.organizer, customer=customer)
r = client.post(
f'/bigevents/account/activate?id={customer.identifier}&token={TokenGenerator().make_token(customer)}', {
@@ -128,10 +123,7 @@ def test_org_register(env, client, mocker):
@pytest.mark.django_db
def test_org_register_duplicate_email(env, client, mocker):
from pretix.base.signals import customer_created
mocker.patch('pretix.base.signals.customer_created.send')
def test_org_register_duplicate_email(env, client):
with scopes_disabled():
env[0].customers.create(email='john@example.org')
r = client.post('/bigevents/account/register', {
@@ -140,7 +132,6 @@ def test_org_register_duplicate_email(env, client, mocker):
})
assert b'already registered' in r.content
assert r.status_code == 200
customer_created.send.assert_not_called()
@pytest.mark.django_db
@@ -176,11 +167,7 @@ def test_org_activate_invalid_token(env, client):
@pytest.mark.django_db
def test_org_login_logout(env, client, mocker):
from pretix.base.signals import customer_signed_in
mocker.patch('pretix.base.signals.customer_signed_in.send')
customer = None
def test_org_login_logout(env, client):
with scopes_disabled():
customer = env[0].customers.create(email='john@example.org', is_verified=True)
customer.set_password('foo')
@@ -192,8 +179,6 @@ def test_org_login_logout(env, client, mocker):
})
assert r.status_code == 302
customer_signed_in.send.assert_called_once_with(customer.organizer, customer=customer)
r = client.get('/bigevents/account/')
assert r.status_code == 200
@@ -205,10 +190,7 @@ def test_org_login_logout(env, client, mocker):
@pytest.mark.django_db
def test_org_login_invalid_password(env, client, mocker):
from pretix.base.signals import customer_signed_in
mocker.patch('pretix.base.signals.customer_signed_in.send')
def test_org_login_invalid_password(env, client):
with scopes_disabled():
customer = env[0].customers.create(email='john@example.org', is_verified=True)
customer.set_password('foo')
@@ -220,15 +202,10 @@ def test_org_login_invalid_password(env, client, mocker):
})
assert r.status_code == 200
assert b'alert-danger' in r.content
customer_signed_in.send.assert_not_called()
@pytest.mark.django_db
def test_org_login_not_verified(env, client, mocker):
from pretix.base.signals import customer_signed_in
mocker.patch('pretix.base.signals.customer_signed_in.send')
customer = None
def test_org_login_not_verified(env, client):
with scopes_disabled():
customer = env[0].customers.create(email='john@example.org', is_verified=False)
customer.set_password('foo')
@@ -240,14 +217,10 @@ def test_org_login_not_verified(env, client, mocker):
})
assert r.status_code == 200
assert b'alert-danger' in r.content
customer_signed_in.send.assert_not_called()
@pytest.mark.django_db
def test_org_login_not_active(env, client, mocker):
from pretix.base.signals import customer_signed_in
mocker.patch('pretix.base.signals.customer_signed_in.send')
def test_org_login_not_active(env, client):
with scopes_disabled():
customer = env[0].customers.create(email='john@example.org', is_verified=True, is_active=False)
customer.set_password('foo')
@@ -259,7 +232,6 @@ def test_org_login_not_active(env, client, mocker):
})
assert r.status_code == 200
assert b'alert-danger' in r.content
customer_signed_in.send.assert_not_called()
@pytest.fixture
@@ -337,18 +309,12 @@ def _sso_login(client, provider, email='test@example.org', popup_origin=None, ex
@pytest.mark.django_db
def test_org_sso_login_new_customer(env, client, provider, mocker):
from pretix.base.signals import customer_created, customer_signed_in
mocker.patch('pretix.base.signals.customer_created.send')
mocker.patch('pretix.base.signals.customer_signed_in.send')
def test_org_sso_login_new_customer(env, client, provider):
_sso_login(client, provider)
with scopes_disabled():
c = Customer.objects.get(provider=provider)
assert c.external_identifier == "abcdf"
customer_created.send.assert_called_once_with(c.organizer, customer=c)
customer_signed_in.send.assert_called_once_with(c.organizer, customer=c)
r = client.get('/bigevents/account/')
assert r.status_code == 200
@@ -386,15 +352,10 @@ def test_org_sso_login_new_customer_popup_invalid_origin(env, client, provider):
@pytest.mark.django_db
def test_org_sso_login_returning_customer_new_email(env, client, provider, mocker):
from pretix.base.signals import customer_signed_in
mocker.patch('pretix.base.signals.customer_signed_in.send')
def test_org_sso_login_returning_customer_new_email(env, client, provider):
_sso_login(client, provider)
with scopes_disabled():
c = Customer.objects.get(provider=provider)
customer_signed_in.send.assert_called_once_with(c.organizer, customer=c)
customer_signed_in.send.reset_mock()
r = client.get('/bigevents/account/logout')
assert r.status_code == 302
@@ -402,7 +363,6 @@ def test_org_sso_login_returning_customer_new_email(env, client, provider, mocke
_sso_login(client, provider, 'new@example.net')
c.refresh_from_db()
assert c.email == "new@example.net"
customer_signed_in.send.assert_called_once_with(c.organizer, customer=c)
@pytest.mark.django_db(transaction=True)