Compare commits

..

7 Commits

Author SHA1 Message Date
Martin Gross
de1e7abc48 base/pdf: Only call get_fonts() once per generation 2024-03-08 11:23:31 +01:00
Martin Gross
5fa5810670 Invoices: Add support for event-fonts 2024-02-28 22:03:43 +01:00
Martin Gross
572fcf4751 Editor/webfonts.css: Do not create a font section for "pdf_only" 2024-02-28 21:46:33 +01:00
Martin Gross
c11f718253 isort 2024-02-28 21:31:39 +01:00
Martin Gross
7949be15b7 Review Comments: Do not run signal for every single textbox on page 2024-02-28 21:30:03 +01:00
Martin Gross
cc020f24a2 Review Comments: Check for // instead of https for web-webfonts 2024-02-28 21:21:52 +01:00
Martin Gross
f0a76a3ee0 Add register_event_fonts signal and facilities for web-embedded webfonts 2024-02-13 10:20:12 +01:00
166 changed files with 18622 additions and 20211 deletions

View File

@@ -345,7 +345,7 @@ to speed up various operations::
The location of redis, as a URL of the form ``redis://[:password]@localhost:6379/0``
or ``unix://[:password]@/path/to/socket.sock?db=0``
``sessions``
``session``
When this is set to ``True``, redis will be used as the session storage.
``sentinels``
@@ -521,4 +521,4 @@ pretix can optionally make use of a GeoIP database for some features. It needs a
.. _GeoAcumen: https://github.com/geoacumen/geoacumen-country
.. _GeoLite2: https://dev.maxmind.com/geoip/geolite2-free-geolocation-data
.. _GeoLite2: https://dev.maxmind.com/geoip/geolite2-free-geolocation-data

View File

@@ -94,9 +94,7 @@ If you want the user to return to your application after the payment is complete
"Plugins". Enable the plugin "Redirection from order page". Then, go to the new page "Settings", then "Redirection".
Enter the base URL of your web application. This will allow you to redirect to pages under this base URL later on.
For example, if you want users to be redirected to ``https://example.org/order/return?tx_id=1234``, you could now
either enter ``https://example.org/order/`` or ``https://example.org/``.
Please note that in the latter case the trailing slash is required, ``https://example.org`` is not allowed to prevent.
Only base URLs with a secure (``https://``) or local (``http://localhost``) origin are permitted.
either enter ``https://example.org`` or ``https://example.org/order/``.
The user will be redirected back to your page instead of pretix' order confirmation page after the payment,
**regardless of whether it was successful or not**. We will append an ``error=…`` query parameter with an error

View File

@@ -179,11 +179,6 @@ country string Attendee countr
state string Attendee state (ISO 3166-2 code). Only supported in
AU, BR, CA, CN, MY, MX, and US, otherwise ``null``.
voucher integer Internal ID of the voucher used for this position (or ``null``)
voucher_budget_use money (string) Amount of money discounted by the voucher, corresponding
to how much of the ``budget`` of the voucher is consumed.
**Important:** Do not rely on this amount to be a useful
value if the position's price, product or voucher
are changed *after* the order was created. Can be ``null``.
tax_rate decimal (string) VAT rate applied for this position
tax_value money (string) VAT included in this position
tax_rule integer The ID of the used tax rule (or ``null``)
@@ -372,7 +367,6 @@ List of all orders
"country": "DE",
"state": null,
"voucher": null,
"voucher_budget_use": null,
"tax_rate": "0.00",
"tax_value": "0.00",
"tax_rule": null,
@@ -595,7 +589,6 @@ Fetching individual orders
"country": "DE",
"state": null,
"voucher": null,
"voucher_budget_use": null,
"tax_rate": "0.00",
"tax_rule": null,
"tax_value": "0.00",
@@ -1548,7 +1541,6 @@ List of all order positions
},
"attendee_email": null,
"voucher": null,
"voucher_budget_use": null,
"tax_rate": "0.00",
"tax_rule": null,
"tax_value": "0.00",
@@ -1662,7 +1654,6 @@ Fetching individual positions
},
"attendee_email": null,
"voucher": null,
"voucher_budget_use": null,
"tax_rate": "0.00",
"tax_rule": null,
"tax_value": "0.00",

View File

@@ -84,6 +84,8 @@ convenient to you:
.. automethod:: _register_fonts
.. automethod:: _register_event_fonts
.. automethod:: _on_first_page
.. automethod:: _on_other_page

View File

@@ -34,7 +34,6 @@ internal_id string Can be used for
contact_name string Contact person (or ``null``)
contact_name_parts object of strings Decomposition of contact name (i.e. given name, family name)
contact_email string Contact person email address (or ``null``)
contact_cc_email string Copy email addresses, can be multiple separated by comma (or ``null``)
booth string Booth number (or ``null``). Maximum 100 characters.
locale string Locale for communication with the exhibitor.
access_code string Access code for the exhibitor to access their data or use the lead scanning app (read-only).
@@ -110,7 +109,6 @@ Endpoints
"title": "Dr"
},
"contact_email": "johnson@as.example.org",
"contact_cc_email": "miller@as.example.org,smith@as.example.org",
"booth": "A2",
"locale": "de",
"access_code": "VKHZ2FU84",
@@ -164,7 +162,6 @@ Endpoints
"title": "Dr"
},
"contact_email": "johnson@as.example.org",
"contact_cc_email": "miller@as.example.org,smith@as.example.org",
"booth": "A2",
"locale": "de",
"access_code": "VKHZ2FU84",
@@ -368,7 +365,6 @@ Endpoints
"title": "Dr"
},
"contact_email": "johnson@as.example.org",
"contact_cc_email": "miller@as.example.org,smith@as.example.org",
"booth": "A2",
"locale": "de",
"allow_lead_scanning": true,
@@ -398,7 +394,6 @@ Endpoints
"title": "Dr"
},
"contact_email": "johnson@as.example.org",
"contact_cc_email": "miller@as.example.org,smith@as.example.org",
"booth": "A2",
"locale": "de",
"access_code": "VKHZ2FU84",
@@ -459,7 +454,6 @@ Endpoints
"title": "Dr"
},
"contact_email": "johnson@as.example.org",
"contact_cc_email": "miller@as.example.org,smith@as.example.org",
"booth": "A2",
"locale": "de",
"access_code": "VKHZ2FU84",

View File

@@ -1,4 +1,4 @@
sphinx==7.2.*
sphinx==7.0.*
jinja2==3.1.*
sphinx-rtd-theme
sphinxcontrib-httpdomain

View File

@@ -1,5 +1,5 @@
-e ../
sphinx==7.2.*
sphinx==7.0.*
jinja2==3.1.*
sphinx-rtd-theme
sphinxcontrib-httpdomain

View File

@@ -19,3 +19,4 @@ Then, head to the **Bundled products** tab of the "conference ticket" and add th
Once a customer tries to buy the € 450 conference ticket, a sub-product will be added and the price will automatically be split into the two components, leading to a correct computation of taxes.
You can find more use cases in these specialized guides:

View File

@@ -31,41 +31,41 @@ dependencies = [
"BeautifulSoup4==4.12.*",
"bleach==5.0.*",
"celery==5.3.*",
"chardet==5.2.*",
"chardet==5.1.*",
"cryptography>=3.4.2",
"css-inline==0.13.*",
"css-inline==0.8.*",
"defusedcsv>=1.1.0",
"dj-static",
"Django==4.2.*",
"django-bootstrap3==23.6.*",
"django-compressor==4.4",
"django-bootstrap3==23.1.*",
"django-compressor==4.3.*",
"django-countries==7.5.*",
"django-filter==23.5",
"django-filter==23.2",
"django-formset-js-improved==0.5.0.3",
"django-formtools==2.5.1",
"django-hierarkey==1.1.*",
"django-hijack==3.4.*",
"django-hijack==3.3.*",
"django-i18nfield==1.9.*,>=1.9.4",
"django-libsass==0.9",
"django-localflavor==4.0",
"django-markup",
"django-oauth-toolkit==2.3.*",
"django-otp==1.3.*",
"django-phonenumber-field==7.3.*",
"django-oauth-toolkit==2.2.*",
"django-otp==1.2.*",
"django-phonenumber-field==7.1.*",
"django-redis==5.4.*",
"django-scopes==2.0.*",
"django-statici18n==2.4.*",
"djangorestframework==3.14.*",
"dnspython==2.5.*",
"dnspython==2.3.*",
"drf_ujson2==1.7.*",
"geoip2==4.*",
"importlib_metadata==7.*", # Polyfill, we can probably drop this once we require Python 3.10+
"isoweek",
"jsonschema",
"kombu==5.3.*",
"libsass==0.23.*",
"libsass==0.22.*",
"lxml",
"markdown==3.5.2", # 3.3.5 requires importlib-metadata>=4.4, but django-bootstrap3 requires importlib-metadata<3.
"markdown==3.4.3", # 3.3.5 requires importlib-metadata>=4.4, but django-bootstrap3 requires importlib-metadata<3.
# We can upgrade markdown again once django-bootstrap3 upgrades or once we drop Python 3.6 and 3.7
"mt-940==4.30.*",
"oauthlib==3.2.*",
@@ -73,24 +73,24 @@ dependencies = [
"packaging",
"paypalrestsdk==1.13.*",
"paypal-checkout-serversdk==1.0.*",
"PyJWT==2.8.*",
"PyJWT==2.7.*",
"phonenumberslite==8.13.*",
"Pillow==10.2.*",
"Pillow==9.5.*",
"pretix-plugin-build",
"protobuf==4.25.*",
"protobuf==4.23.*",
"psycopg2-binary",
"pycountry",
"pycparser==2.21",
"pycryptodome==3.20.*",
"pycryptodome==3.18.*",
"pypdf==3.9.*",
"python-bidi==0.4.*", # Support for Arabic in reportlab
"python-dateutil==2.9.*",
"python-dateutil==2.8.*",
"pytz",
"pytz-deprecation-shim==0.1.*",
"pyuca",
"qrcode==7.4.*",
"redis==5.0.*",
"reportlab==4.1.*",
"redis==4.6.*",
"reportlab==4.0.*",
"requests==2.31.*",
"sentry-sdk==1.40.*",
"sepaxml==2.6.*",
@@ -102,7 +102,7 @@ dependencies = [
"tqdm==4.*",
"vat_moss_forked==2020.3.20.0.11.0",
"vobject==0.9.*",
"webauthn==2.0.*",
"webauthn==0.4.*",
"zeep==4.2.*"
]
@@ -112,21 +112,23 @@ dev = [
"aiohttp==3.9.*",
"coverage",
"coveralls",
"fakeredis==2.21.*",
"flake8==7.0.*",
"fakeredis==2.18.*",
"flake8==6.0.*",
"freezegun",
"isort==5.13.*",
"isort==5.12.*",
"pep8-naming==0.13.*",
"potypo",
"pycodestyle==2.10.*",
"pyflakes==3.0.*",
"pytest-asyncio",
"pytest-cache",
"pytest-cov",
"pytest-django==4.*",
"pytest-mock==3.12.*",
"pytest-mock==3.10.*",
"pytest-rerunfailures==13.*",
"pytest-sugar",
"pytest-xdist==3.5.*",
"pytest==8.0.*",
"pytest-xdist==3.3.*",
"pytest==7.3.*",
"responses",
]

View File

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

View File

@@ -1,18 +0,0 @@
# Generated by Django 4.2.10 on 2024-02-12 11:52
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("pretixapi", "0011_bigint"),
]
operations = [
migrations.AddField(
model_name="oauthapplication",
name="post_logout_redirect_uris",
field=models.TextField(default=""),
),
]

View File

@@ -42,11 +42,6 @@ class OAuthApplication(AbstractApplication):
verbose_name=_("Redirection URIs"),
help_text=_("Allowed URIs list, space separated")
)
post_logout_redirect_uris = models.TextField(
blank=True, validators=[URIValidator],
help_text=_("Allowed Post Logout URIs list, space separated"),
default="",
)
client_id = models.CharField(
verbose_name=_("Client ID"),
max_length=100, unique=True, default=generate_client_id, db_index=True

View File

@@ -486,11 +486,11 @@ class OrderPositionSerializer(I18nAwareModelSerializer):
'company', 'street', 'zipcode', 'city', 'country', 'state', 'discount',
'attendee_email', 'voucher', 'tax_rate', 'tax_value', 'secret', 'addon_to', 'subevent', 'checkins',
'downloads', 'answers', 'tax_rule', 'pseudonymization_id', 'pdf_data', 'seat', 'canceled',
'valid_from', 'valid_until', 'blocked', 'voucher_budget_use')
'valid_from', 'valid_until', 'blocked')
read_only_fields = (
'id', 'order', 'positionid', 'item', 'variation', 'price', 'voucher', 'tax_rate', 'tax_value', 'secret',
'addon_to', 'subevent', 'checkins', 'downloads', 'answers', 'tax_rule', 'pseudonymization_id', 'pdf_data',
'seat', 'canceled', 'discount', 'valid_from', 'valid_until', 'blocked', 'voucher_budget_use'
'seat', 'canceled', 'discount', 'valid_from', 'valid_until', 'blocked'
)
def __init__(self, *args, **kwargs):

View File

@@ -35,7 +35,6 @@ from django.http import Http404
from django.shortcuts import get_object_or_404
from django.utils.functional import cached_property
from django.utils.timezone import now
from django.utils.translation import gettext
from django_filters.rest_framework import DjangoFilterBackend, FilterSet
from django_scopes import scopes_disabled
from packaging.version import parse
@@ -587,32 +586,6 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
'list': MiniCheckinListSerializer(list_by_event[revoked_matches[0].event_id]).data,
}, status=400)
else:
if media.linked_orderposition.order.event_id not in list_by_event:
# Medium exists but connected ticket is for the wrong event
if not simulate:
checkinlists[0].event.log_action('pretix.event.checkin.unknown', data={
'datetime': datetime,
'type': checkin_type,
'list': checkinlists[0].pk,
'barcode': raw_barcode,
'searched_lists': [cl.pk for cl in checkinlists]
}, user=user, auth=auth)
Checkin.objects.create(
position=None,
successful=False,
error_reason=Checkin.REASON_INVALID,
error_explanation=gettext('Medium connected to other event'),
**common_checkin_args,
)
return Response({
'detail': 'Not found.', # for backwards compatibility
'status': 'error',
'reason': Checkin.REASON_INVALID,
'reason_explanation': gettext('Medium connected to other event'),
'require_attention': False,
'checkin_texts': [],
'list': MiniCheckinListSerializer(checkinlists[0]).data,
}, status=404)
op_candidates = [media.linked_orderposition]
if list_by_event[media.linked_orderposition.order.event_id].addon_match:
op_candidates += list(media.linked_orderposition.addons.all())

View File

@@ -189,7 +189,7 @@ class TemplateBasedMailRenderer(BaseHTMLMailRenderer):
tpl = get_template(self.template_name)
body_html = tpl.render(htmlctx)
inliner = css_inline.CSSInliner(keep_style_tags=False)
inliner = css_inline.CSSInliner(remove_style_tags=True)
body_html = inliner.inline(body_html)
return body_html

View File

@@ -86,7 +86,6 @@ class InvoiceExporterMixin:
('', _('All payment providers')),
] + [
(k, v.verbose_name) for k, v in self.event.get_payment_providers().items()
if not v.is_meta
],
required=False,
help_text=_('Only include invoices for orders that have at least one payment attempt '

View File

@@ -257,8 +257,8 @@ class OrderListExporter(MultiSheetListExporter):
tax_rates = self._get_all_tax_rates(qs)
headers = [
_('Event slug'), _('Event name'), _('Order code'), _('Order total'), _('Status'), _('Email'),
_('Phone number'), _('Order date'), _('Order time'), _('Company'), _('Name'),
_('Event slug'), _('Order code'), _('Order total'), _('Status'), _('Email'), _('Phone number'),
_('Order date'), _('Order time'), _('Company'), _('Name'),
]
name_scheme = PERSON_NAME_SCHEMES[self.event.settings.name_scheme] if not self.is_multievent else None
if name_scheme and len(name_scheme['fields']) > 1:
@@ -335,7 +335,6 @@ class OrderListExporter(MultiSheetListExporter):
row = [
self.event_object_cache[order.event_id].slug,
str(self.event_object_cache[order.event_id].name),
order.code,
order.total,
order.get_extended_status_display(),
@@ -437,7 +436,6 @@ class OrderListExporter(MultiSheetListExporter):
headers = [
_('Event slug'),
_('Event name'),
_('Order code'),
_('Status'),
_('Email'),
@@ -474,7 +472,6 @@ class OrderListExporter(MultiSheetListExporter):
tz = ZoneInfo(order.event.settings.timezone)
row = [
self.event_object_cache[order.event_id].slug,
str(self.event_object_cache[order.event_id].name),
order.code,
_("canceled") if op.canceled else order.get_extended_status_display(),
order.email,
@@ -553,7 +550,6 @@ class OrderListExporter(MultiSheetListExporter):
headers = [
_('Event slug'),
_('Event name'),
_('Order code'),
_('Position ID'),
_('Status'),
@@ -655,7 +651,6 @@ class OrderListExporter(MultiSheetListExporter):
tz = ZoneInfo(self.event_object_cache[order.event_id].settings.timezone)
row = [
self.event_object_cache[order.event_id].slug,
str(self.event_object_cache[order.event_id].name),
order.code,
op.positionid,
_("canceled") if op.canceled else order.get_extended_status_display(),

View File

@@ -0,0 +1,63 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
from bootstrap3.renderers import (
FieldRenderer as BaseFieldRenderer,
InlineFieldRenderer as BaseInlineFieldRenderer,
)
from django.forms import (
CheckboxInput, CheckboxSelectMultiple, ClearableFileInput, RadioSelect,
SelectDateWidget,
)
class FieldRenderer(BaseFieldRenderer):
# Local application of https://github.com/zostera/django-bootstrap3/pull/859
def post_widget_render(self, html):
if isinstance(self.widget, CheckboxSelectMultiple):
html = self.list_to_class(html, "checkbox")
elif isinstance(self.widget, RadioSelect):
html = self.list_to_class(html, "radio")
elif isinstance(self.widget, SelectDateWidget):
html = self.fix_date_select_input(html)
elif isinstance(self.widget, ClearableFileInput):
html = self.fix_clearable_file_input(html)
elif isinstance(self.widget, CheckboxInput):
html = self.put_inside_label(html)
return html
class InlineFieldRenderer(BaseInlineFieldRenderer):
# Local application of https://github.com/zostera/django-bootstrap3/pull/859
def post_widget_render(self, html):
if isinstance(self.widget, CheckboxSelectMultiple):
html = self.list_to_class(html, "checkbox")
elif isinstance(self.widget, RadioSelect):
html = self.list_to_class(html, "radio")
elif isinstance(self.widget, SelectDateWidget):
html = self.fix_date_select_input(html)
elif isinstance(self.widget, ClearableFileInput):
html = self.fix_clearable_file_input(html)
elif isinstance(self.widget, CheckboxInput):
html = self.put_inside_label(html)
return html

View File

@@ -182,7 +182,7 @@ class BaseReportlabInvoiceRenderer(BaseInvoiceRenderer):
pdfmetrics.registerFontFamily('OpenSans', normal='OpenSans', bold='OpenSansBd',
italic='OpenSansIt', boldItalic='OpenSansBI')
for family, styles in get_fonts().items():
for family, styles in get_fonts(event=self.event, pdf_support_required=True).items():
if family == self.event.settings.invoice_renderer_font:
pdfmetrics.registerFont(TTFont(family, finders.find(styles['regular']['truetype'])))
self.font_regular = family

View File

@@ -20,7 +20,7 @@
# <https://www.gnu.org/licenses/>.
#
from collections import OrderedDict
from urllib.parse import urlsplit
from urllib.parse import urlparse, urlsplit
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
from django.conf import settings
@@ -40,6 +40,7 @@ from pretix.base.settings import global_settings_object
from pretix.multidomain.urlreverse import (
get_event_domain, get_organizer_domain,
)
from pretix.presale.style import get_fonts
_supported = None
@@ -240,6 +241,14 @@ class SecurityMiddleware(MiddlewareMixin):
)
def process_response(self, request, resp):
def nested_dict_values(d):
for v in d.values():
if isinstance(v, dict):
yield from nested_dict_values(v)
else:
if isinstance(v, str):
yield v
url = resolve(request.path_info)
if settings.DEBUG and resp.status_code >= 400:
@@ -259,6 +268,14 @@ class SecurityMiddleware(MiddlewareMixin):
if gs.settings.leaflet_tiles:
img_src.append(gs.settings.leaflet_tiles[:gs.settings.leaflet_tiles.index("/", 10)].replace("{s}", "*"))
font_src = set()
if hasattr(request, 'event'):
for font in get_fonts(request.event, pdf_support_required=False).values():
for path in list(nested_dict_values(font)):
font_location = urlparse(path)
if font_location.scheme and font_location.netloc:
font_src.add('{}://{}'.format(font_location.scheme, font_location.netloc))
h = {
'default-src': ["{static}"],
'script-src': ['{static}'],
@@ -267,7 +284,7 @@ class SecurityMiddleware(MiddlewareMixin):
'style-src': ["{static}", "{media}"],
'connect-src': ["{dynamic}", "{media}"],
'img-src': ["{static}", "{media}", "data:"] + img_src,
'font-src': ["{static}"],
'font-src': ["{static}"] + list(font_src),
'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

View File

@@ -1,19 +0,0 @@
# Generated by Django 4.2.9 on 2024-01-30 11:11
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("pretixbase", "0256_itemvariation_unavail_modes"),
]
operations = [
migrations.AlterField(
model_name="item",
name="default_price",
field=models.DecimalField(decimal_places=2, default=0, max_digits=13),
preserve_default=False,
),
]

View File

@@ -37,7 +37,9 @@ import json
import operator
from datetime import timedelta
from functools import reduce
from urllib.parse import urlparse
import webauthn
from django.conf import settings
from django.contrib.auth.models import (
AbstractBaseUser, BaseUserManager, PermissionsMixin,
@@ -51,12 +53,11 @@ from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
from django_otp.models import Device
from django_scopes import scopes_disabled
from webauthn.helpers.structs import PublicKeyCredentialDescriptor
from pretix.base.i18n import language
from pretix.helpers.urls import build_absolute_uri
from ...helpers.u2f import pub_key_from_der, websafe_decode
from ...helpers.u2f import pub_key_from_der, websafe_decode, websafe_encode
from .base import LoggingMixin
@@ -605,12 +606,7 @@ class U2FDevice(Device):
json_data = models.TextField()
@property
def webauthndevice(self):
d = json.loads(self.json_data)
return PublicKeyCredentialDescriptor(websafe_decode(d['keyHandle']))
@property
def webauthnpubkey(self):
def webauthnuser(self):
d = json.loads(self.json_data)
# We manually need to convert the pubkey from DER format (used in our
# former U2F implementation) to the format required by webauthn. This
@@ -622,7 +618,16 @@ class U2FDevice(Device):
pub_key.public_numbers().x, pub_key.public_numbers().y
)
)
return pub_key
return webauthn.WebAuthnUser(
d['keyHandle'],
self.user.email,
str(self.user),
settings.SITE_URL,
d['keyHandle'],
websafe_encode(pub_key),
1,
urlparse(settings.SITE_URL).netloc
)
class WebAuthnDevice(Device):
@@ -634,9 +639,14 @@ class WebAuthnDevice(Device):
sign_count = models.IntegerField(default=0)
@property
def webauthndevice(self):
return PublicKeyCredentialDescriptor(websafe_decode(self.credential_id))
@property
def webauthnpubkey(self):
return websafe_decode(self.pub_key)
def webauthnuser(self):
return webauthn.WebAuthnUser(
self.ukey,
self.user.email,
str(self.user),
settings.SITE_URL,
self.credential_id,
self.pub_key,
self.sign_count,
urlparse(settings.SITE_URL).netloc
)

View File

@@ -265,9 +265,6 @@ class EventMixin:
@property
def event_microdata(self):
if self.settings.event_microdata:
return self.settings.event_microdata
import json
eventdict = {

View File

@@ -430,7 +430,7 @@ class Item(LoggedModel):
help_text=_("If this product has multiple variations, you can set different prices for each of the "
"variations. If a variation does not have a special price or if you do not have variations, "
"this price will be used."),
max_digits=13, decimal_places=2,
max_digits=13, decimal_places=2, null=True
)
free_price = models.BooleanField(
default=False,

View File

@@ -351,6 +351,9 @@ class Voucher(LoggedModel):
'variations.'))
if variation and not item.variations.filter(pk=variation.pk).exists():
raise ValidationError(_('This variation does not belong to this product.'))
if item.has_variations and not variation and data.get('block_quota'):
raise ValidationError(_('You can only block quota if you specify a specific product variation. '
'Otherwise it might be unclear which quotas to block.'))
if item.category and item.category.is_addon:
raise ValidationError(_('It is currently not possible to create vouchers for add-on products.'))
elif block_quota:
@@ -428,15 +431,7 @@ class Voucher(LoggedModel):
elif old_instance.variation:
quotas |= set(old_instance.variation.quotas.filter(subevent=old_instance.subevent))
elif old_instance.item:
if old_instance.item.has_variations:
quotas |= set(
Quota.objects.filter(pk__in=Quota.variations.through.objects.filter(
itemvariation__item=old_instance.item,
quota__subevent=old_instance.subevent,
).values('quota_id'))
)
else:
quotas |= set(old_instance.item.quotas.filter(subevent=old_instance.subevent))
quotas |= set(old_instance.item.quotas.filter(subevent=old_instance.subevent))
return quotas
@staticmethod
@@ -451,19 +446,13 @@ class Voucher(LoggedModel):
if quota:
new_quotas = {quota}
elif item and item.has_variations and not variation:
raise ValidationError(_('You can only block quota if you specify a specific product variation. '
'Otherwise it might be unclear which quotas to block.'))
elif item and variation:
new_quotas = set(variation.quotas.filter(subevent=data.get('subevent')))
elif item and not item.has_variations:
new_quotas = set(item.quotas.filter(subevent=data.get('subevent')))
elif item and item.has_variations:
new_quotas = set(
Quota.objects.filter(
pk__in=Quota.variations.through.objects.filter(
itemvariation__item=old_instance.item,
quota__subevent=data.get('subevent'),
).values('quota_id')
)
)
else:
raise ValidationError(_('You need to select a specific product or quota if this voucher should reserve '
'tickets.'))

View File

@@ -78,7 +78,7 @@ from reportlab.pdfgen.canvas import Canvas
from reportlab.platypus import Paragraph
from pretix.base.i18n import language
from pretix.base.models import Order, OrderPosition, Question
from pretix.base.models import Event, Order, OrderPosition, Question
from pretix.base.settings import PERSON_NAME_SCHEMES
from pretix.base.signals import layout_image_variables, layout_text_variables
from pretix.base.templatetags.money import money_filter
@@ -738,9 +738,10 @@ class Renderer:
else:
self.bg_bytes = None
self.bg_pdf = None
self.event_fonts = list(get_fonts(event, pdf_support_required=True).keys()) + ['Open Sans']
@classmethod
def _register_fonts(cls):
def _register_fonts(cls, event: Event = None):
if hasattr(cls, '_fonts_registered'):
return
pdfmetrics.registerFont(TTFont('Open Sans', finders.find('fonts/OpenSans-Regular.ttf')))
@@ -748,7 +749,7 @@ class Renderer:
pdfmetrics.registerFont(TTFont('Open Sans B', finders.find('fonts/OpenSans-Bold.ttf')))
pdfmetrics.registerFont(TTFont('Open Sans B I', finders.find('fonts/OpenSans-BoldItalic.ttf')))
for family, styles in get_fonts().items():
for family, styles in get_fonts(event, pdf_support_required=True).items():
pdfmetrics.registerFont(TTFont(family, finders.find(styles['regular']['truetype'])))
if 'italic' in styles:
pdfmetrics.registerFont(TTFont(family + ' I', finders.find(styles['italic']['truetype'])))
@@ -939,6 +940,12 @@ class Renderer:
if o['italic']:
font += ' I'
# Since pdfmetrics.registerFont is global, we want to make sure that no one tries to sneak in a font, they
# should not have access to.
if font not in self.event_fonts:
logger.warning(f'Unauthorized use of font "{font}"')
font = 'Open Sans'
try:
ad = getAscentDescent(font, float(o['fontsize']))
except KeyError: # font not known, fall back

View File

@@ -104,10 +104,10 @@ def build_invoice(invoice: Invoice) -> Invoice:
expire_date=date_format(invoice.order.expires, "SHORT_DATE_FORMAT")
)
invoice.introductory_text = str(introductory).replace('\n', '<br />').replace('\r', '')
invoice.additional_text = str(additional).replace('\n', '<br />').replace('\r', '')
invoice.introductory_text = str(introductory).replace('\n', '<br />')
invoice.additional_text = str(additional).replace('\n', '<br />')
invoice.footer_text = str(footer)
invoice.payment_provider_text = str(payment).replace('\n', '<br />').replace('\r', '')
invoice.payment_provider_text = str(payment).replace('\n', '<br />')
invoice.payment_provider_stamp = str(payment_stamp) if payment_stamp else None
try:
@@ -462,10 +462,10 @@ def build_preview_invoice_pdf(event):
footer = event.settings.get('invoice_footer_text', as_type=LazyI18nString)
payment = _("A payment provider specific text might appear here.")
invoice.introductory_text = str(introductory).replace('\n', '<br />').replace('\r', '')
invoice.additional_text = str(additional).replace('\n', '<br />').replace('\r', '')
invoice.introductory_text = str(introductory).replace('\n', '<br />')
invoice.additional_text = str(additional).replace('\n', '<br />')
invoice.footer_text = str(footer)
invoice.payment_provider_text = str(payment).replace('\n', '<br />').replace('\r', '')
invoice.payment_provider_text = str(payment).replace('\n', '<br />')
invoice.payment_provider_stamp = _('paid')
invoice.invoice_to_name = _("John Doe")
invoice.invoice_to_street = _("214th Example Street")
@@ -488,7 +488,7 @@ def build_preview_invoice_pdf(event):
InvoiceLine.objects.create(
invoice=invoice, description=_("Sample product {}").format(i + 1),
gross_value=tax.gross, tax_value=tax.tax,
tax_rate=tax.rate, tax_name=tax.name
tax_rate=tax.rate
)
else:
for i in range(5):

View File

@@ -383,7 +383,6 @@ def mail_send_task(self, *args, to: List[str], subject: str, body: str, html: st
if event:
with scopes_disabled():
event = Event.objects.get(id=event)
organizer = event.organizer
backend = event.get_mail_backend()
cm = lambda: scope(organizer=event.organizer) # noqa
elif organizer:

View File

@@ -136,7 +136,7 @@ def send_notification_mail(notification: Notification, user: User):
tpl_html = get_template('pretixbase/email/notification.html')
body_html = tpl_html.render(ctx)
inliner = css_inline.CSSInliner(keep_style_tags=False)
inliner = css_inline.CSSInliner(remove_style_tags=True)
body_html = inliner.inline(body_html)
tpl_plain = get_template('pretixbase/email/notification.txt')

View File

@@ -90,8 +90,8 @@ class QuotaAvailability:
self._count_waitinglist = count_waitinglist
self._ignore_closed = ignore_closed
self._full_results = full_results
self._item_to_quotas = defaultdict(set)
self._var_to_quotas = defaultdict(set)
self._item_to_quotas = defaultdict(list)
self._var_to_quotas = defaultdict(list)
self._early_out = early_out
self._quota_objects = {}
self.results = {}
@@ -243,16 +243,13 @@ class QuotaAvailability:
quota_id__in=[q.pk for q in quotas]
).values('quota_id', 'item_id')
for m in q_items:
self._item_to_quotas[m['item_id']].add(self._quota_objects[m['quota_id']])
self._item_to_quotas[m['item_id']].append(self._quota_objects[m['quota_id']])
q_vars = Quota.variations.through.objects.filter(
quota_id__in=[q.pk for q in quotas]
).values('quota_id', 'itemvariation_id', 'itemvariation__item_id')
).values('quota_id', 'itemvariation_id')
for m in q_vars:
self._var_to_quotas[m['itemvariation_id']].add(self._quota_objects[m['quota_id']])
# We can't be 100% certain that a quota, when it is connected to a variation, is also always connected to
# the parent item, so we double-check here just to be sure.
self._item_to_quotas[m['itemvariation__item_id']].add(self._quota_objects[m['quota_id']])
self._var_to_quotas[m['itemvariation_id']].append(self._quota_objects[m['quota_id']])
self._compute_orders(quotas, q_items, q_vars, size_left)
@@ -381,10 +378,7 @@ class QuotaAvailability:
Q(
Q(
Q(variation_id__isnull=True) &
Q(item_id__in=(
{i['item_id'] for i in q_items if i['quota_id'] in quota_ids} |
{i['itemvariation__item_id'] for i in q_vars if i['quota_id'] in quota_ids}
))
Q(item_id__in={i['item_id'] for i in q_items if i['quota_id'] in quota_ids})
) | Q(
variation_id__in={i['itemvariation_id'] for i in q_vars if i['quota_id'] in quota_ids}
) | Q(

View File

@@ -89,7 +89,7 @@ def primary_font_kwargs():
choices = [('Open Sans', 'Open Sans')]
choices += sorted([
(a, {"title": a, "data": v}) for a, v in get_fonts().items() if not v.get('pdf_only', False)
(a, {"title": a, "data": v}) for a, v in get_fonts(pdf_support_required=False).items()
], key=lambda a: a[0])
return {
'choices': choices,

View File

@@ -0,0 +1,6 @@
{# this is the version from django 3.x, prior to https://github.com/django/django/commit/5942ab5eb165ee2e759174e297148a40dd855920 so that django-bootstrap3 can keep doing its magic #}
{% with id=widget.attrs.id %}<ul{% if id %} id="{{ id }}"{% endif %}{% if widget.attrs.class %} class="{{ widget.attrs.class }}"{% endif %}>{% for group, options, index in widget.optgroups %}{% if group %}
<li>{{ group }}<ul{% if id %} id="{{ id }}_{{ index }}"{% endif %}>{% endif %}{% for option in options %}
<li>{% include option.template_name with widget=option %}</li>{% endfor %}{% if group %}
</ul></li>{% endif %}{% endfor %}
</ul>{% endwith %}

View File

@@ -123,6 +123,8 @@ class ClearableBasenameFileInput(forms.ClearableFileInput):
@property
def name(self):
if hasattr(self.file, 'display_name'):
return self.file.display_name
return self.file.name
@property
@@ -217,17 +219,15 @@ class ExtValidationMixin:
def clean(self, *args, **kwargs):
data = super().clean(*args, **kwargs)
from ...base.models import CachedFile
if isinstance(data, (UploadedFile, CachedFile)):
filename = data.name if isinstance(data, UploadedFile) else data.filename
if isinstance(data, UploadedFile):
filename = data.name
ext = os.path.splitext(filename)[1]
ext = ext.lower()
if ext not in self.ext_whitelist:
raise forms.ValidationError(_("Filetype not allowed!"))
if ext in IMAGE_EXTS:
validate_uploaded_file_for_valid_image(data if isinstance(data, UploadedFile) else data.file)
validate_uploaded_file_for_valid_image(data)
return data
@@ -257,12 +257,6 @@ class CachedFileField(ExtFileField):
if isinstance(data, File):
if hasattr(data, '_uploaded_to'):
return data._uploaded_to
try:
self.clean(data)
except ValidationError:
return None
cf = CachedFile.objects.create(
expires=now() + datetime.timedelta(days=1),
date=now(),
@@ -274,9 +268,6 @@ class CachedFileField(ExtFileField):
cf.save()
data._uploaded_to = cf
return cf
if isinstance(data, CachedFile):
return data
return super().bound_data(data, initial)
def clean(self, *args, **kwargs):

View File

@@ -79,6 +79,7 @@ from pretix.helpers.countries import CachedCountries
from pretix.multidomain.models import KnownDomain
from pretix.multidomain.urlreverse import build_absolute_uri
from pretix.plugins.banktransfer.payment import BankTransfer
from pretix.presale.style import get_fonts
class EventWizardFoundationForm(forms.Form):
@@ -651,6 +652,9 @@ class EventSettingsForm(EventSettingsValidationMixin, FormPlaceholderMixin, Sett
del self.fields['event_list_available_only']
del self.fields['event_list_filters']
del self.fields['event_calendar_future_only']
self.fields['primary_font'].choices += [
(a, {"title": a, "data": v}) for a, v in get_fonts(self.event, pdf_support_required=False).items()
]
# create "virtual" fields for better UX when editing <name>_asked and <name>_required fields
self.virtual_keys = []
@@ -931,6 +935,9 @@ class InvoiceSettingsForm(EventSettingsValidationMixin, SettingsForm):
)
)
self.fields['invoice_generate'].choices = generate_choices
self.fields['invoice_renderer_font'].choices += [
(a, a) for a in get_fonts(event, pdf_support_required=True).keys()
]
def contains_web_channel_validate(val):

View File

@@ -19,7 +19,6 @@
# 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 bootstrap3.renderers import FieldRenderer, InlineFieldRenderer
from bootstrap3.text import text_value
from django.forms import CheckboxInput, CheckboxSelectMultiple, RadioSelect
from django.forms.utils import flatatt
@@ -28,6 +27,8 @@ from django.utils.safestring import mark_safe
from django.utils.translation import pgettext
from i18nfield.forms import I18nFormField
from pretix.base.forms.renderers import FieldRenderer, InlineFieldRenderer
def render_label(content, label_for=None, label_class=None, label_title='', label_id='', optional=False):
"""

View File

@@ -7,7 +7,7 @@
{% block title %}{% trans "General settings" %}{% endblock %}
{% block custom_header %}
{{ block.super }}
<link type="text/css" rel="stylesheet" href="{% url "control:pdf.css" %}">
<link type="text/css" rel="stylesheet" href="{% url "control:pdf.css" organizer=request.organizer.slug event=request.event.slug %}">
{% endblock %}
{% block inside %}
<h1>{% trans "General settings" %}</h1>

View File

@@ -70,10 +70,6 @@
{% endblocktrans %}
</em>
{% endif %}
{% if position.attendee_name %}
<span class="fa fa-user" aria-hidden="true"></span>
{{ position.attendee_name }}
{% endif %}
</h3>
</div>
<div class="panel-body">

View File

@@ -8,7 +8,7 @@
{% compress css %}
<link type="text/css" rel="stylesheet" href="{% static "pretixcontrol/scss/pdfeditor.css" %}">
{% endcompress %}
<link type="text/css" rel="stylesheet" href="{% url "control:pdf.css" %}">
<link type="text/css" rel="stylesheet" href="{% url "control:pdf.css" organizer=request.organizer.slug event=request.event.slug %}">
{% endblock %}
{% block content %}
<h1>
@@ -234,62 +234,7 @@
</select>
</div>
</div>
<div class="row control-group poweredby">
<div class="col-sm-12">
<label>{% trans "Style" %}</label><br>
<select class="input-block-level form-control" id="toolbox-poweredby-style">
<option value="dark">{% trans "Dark" %}</option>
<option value="white">{% trans "Light" %}</option>
</select>
</div>
</div>
<div class="row control-group imagecontent">
<div class="col-sm-12">
<label>{% trans "Image content" %}</label><br>
<select class="input-block-level form-control" id="toolbox-imagecontent">
<option value="">{% trans "Empty" %}</option>
{% for varname, var in images.items %}
<option value="{{ varname }}">{{ var.label }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="row control-group text textcontent">
<div class="col-sm-12">
<label>{% trans "Content" %}</label><br>
<select class="input-block-level form-control" id="toolbox-content">
{% for varname, var in variables.items %}
{% if not var.hidden %}
<option data-sample="{{ var.editor_sample }}" {% if var.migrate_from %}data-old-value="{{ var.migrate_from }}"{% endif %} value="{{ varname }}">{{ var.label }}</option>
{% endif %}
{% endfor %}
{% for p in request.organizer.meta_properties.all %}
<option value="meta:{{ p.name }}">
{% trans "Event attribute:" %} {{ p.name }}
</option>
{% endfor %}
{% for p in request.event.item_meta_properties.all %}
<option value="itemmeta:{{ p.name }}">
{% trans "Item attribute:" %} {{ p.name }}
</option>
{% endfor %}
<option value="other_i18n">{% trans "Other… (multilingual)" %}</option>
<option value="other">{% trans "Other…" %}</option>
</select>
<textarea type="text" value="" class="input-block-level form-control"
id="toolbox-content-other"></textarea>
<div class="i18n-form-group" id="toolbox-content-other-i18n">
{% for l in request.event.settings.locales %}
<textarea id="toolbox-content-other-{{ l }}" rows="3" class="input-block-level form-control" title="{{ l }}" lang="{{ l }}"></textarea>
{% endfor %}
</div>
<p class="help-block" id="toolbox-content-other-help">
<a href="?placeholders=true" target="_blank">{% trans "Show available placeholders" %}</a>
</p>
</div>
</div>
<div class="row control-group position">
<hr/>
<div class="col-sm-6">
<label>{% trans "x (mm)" %}</label><br>
<input type="number" value="13" class="input-block-level form-control" step="0.01"
@@ -332,30 +277,6 @@
</div>
</div>
</div>
<div class="row control-group text">
<div class="col-sm-6">
<label>{% trans "Width (mm)" %}</label><br>
<input type="number" value="13" class="input-block-level form-control" step="0.01"
id="toolbox-textwidth">
</div>
<div class="col-sm-6">
<label>{% trans "Rotation (°)" %}</label><br>
<input type="number" value="0" class="input-block-level form-control" step="0.1"
id="toolbox-textrotation">
</div>
</div>
<div class="row control-group text">
<hr/>
<div class="col-sm-12">
<label>{% trans "Font" %}</label><br>
<select class="input-block-level form-control" id="toolbox-fontfamily">
<option>Open Sans</option>
{% for family in fonts.keys %}
<option>{{ family }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="row control-group text">
<div class="col-sm-6">
<label>{% trans "Font size (pt)" %}</label><br>
@@ -414,6 +335,83 @@
</div>
</div>
</div>
<div class="row control-group text">
<div class="col-sm-12">
<label>{% trans "Font" %}</label><br>
<select class="input-block-level form-control" id="toolbox-fontfamily">
<option>Open Sans</option>
{% for family in fonts.keys %}
<option>{{ family }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="row control-group text">
<div class="col-sm-6">
<label>{% trans "Width (mm)" %}</label><br>
<input type="number" value="13" class="input-block-level form-control" step="0.01"
id="toolbox-textwidth">
</div>
<div class="col-sm-6">
<label>{% trans "Rotation (°)" %}</label><br>
<input type="number" value="0" class="input-block-level form-control" step="0.1"
id="toolbox-textrotation">
</div>
</div>
<div class="row control-group poweredby">
<div class="col-sm-12">
<label>{% trans "Style" %}</label><br>
<select class="input-block-level form-control" id="toolbox-poweredby-style">
<option value="dark">{% trans "Dark" %}</option>
<option value="white">{% trans "Light" %}</option>
</select>
</div>
</div>
<div class="row control-group imagecontent">
<div class="col-sm-12">
<label>{% trans "Image content" %}</label><br>
<select class="input-block-level form-control" id="toolbox-imagecontent">
<option value="">{% trans "Empty" %}</option>
{% for varname, var in images.items %}
<option value="{{ varname }}">{{ var.label }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="row control-group text textcontent">
<div class="col-sm-12">
<label>{% trans "Content" %}</label><br>
<select class="input-block-level form-control" id="toolbox-content">
{% for varname, var in variables.items %}
{% if not var.hidden %}
<option data-sample="{{ var.editor_sample }}" {% if var.migrate_from %}data-old-value="{{ var.migrate_from }}"{% endif %} value="{{ varname }}">{{ var.label }}</option>
{% endif %}
{% endfor %}
{% for p in request.organizer.meta_properties.all %}
<option value="meta:{{ p.name }}">
{% trans "Event attribute:" %} {{ p.name }}
</option>
{% endfor %}
{% for p in request.event.item_meta_properties.all %}
<option value="itemmeta:{{ p.name }}">
{% trans "Item attribute:" %} {{ p.name }}
</option>
{% endfor %}
<option value="other_i18n">{% trans "Other… (multilingual)" %}</option>
<option value="other">{% trans "Other…" %}</option>
</select>
<textarea type="text" value="" class="input-block-level form-control"
id="toolbox-content-other"></textarea>
<div class="i18n-form-group" id="toolbox-content-other-i18n">
{% for l in request.event.settings.locales %}
<textarea id="toolbox-content-other-{{ l }}" rows="3" class="input-block-level form-control" title="{{ l }}" lang="{{ l }}"></textarea>
{% endfor %}
</div>
<p class="help-block" id="toolbox-content-other-help">
<a href="?placeholders=true" target="_blank">{% trans "Show available placeholders" %}</a>
</p>
</div>
</div>
</div>
</div>
<div class="editor-toolbox-text panel panel-default">

View File

@@ -1,4 +1,5 @@
{% load static %}
@font-face {
font-family: 'AND';
font-style: normal;
@@ -14,7 +15,7 @@
{% for family, styles in fonts.items %}
{% for style, formats in styles.items %}
{% if "sample" not in style %}
{% if "sample" not in style and "pdf_only" not in style %}
@font-face {
font-family: '{{ family }}';
{% if style == "italic" or style == "bolditalic" %}
@@ -27,9 +28,9 @@
{% else %}
font-weight: normal;
{% endif %}
src: {% if "woff2" in formats %}url('{% static formats.woff2 %}') format('woff2'),{% endif %}
{% if "woff" in formats %}url('{% static formats.woff %}') format('woff'),{% endif %}
{% if "truetype" in formats %}url('{% static formats.truetype %}') format('truetype'){% endif %};
src: {% if "woff2" in formats %}{% if '//' in formats.woff2 %}url('{{ formats.woff2 }}'){% else %}url('{% static formats.woff2 %}'){% endif %} format('woff2'),{% endif %}
{% if "woff" in formats %}{% if '//' in formats.woff %}url('{{ formats.woff }}'){% else %}url('{% static formats.woff %}'){% endif %} format('woff'),{% endif %}
{% if "truetype" in formats %}{% if '//' in formats.truetype %}url('{{ formats.truetype }}'){% else %}url('{% static formats.truetype %}'){% endif %} format('truetype'){% endif %};
}
.preload-font[data-family="{{family}}"][data-style="{{style}}"] {
font-family: '{{ family }}', 'AND';

View File

@@ -32,11 +32,11 @@
# Unless required by applicable law or agreed to in writing, software distributed under the Apache License 2.0 is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under the License.
import base64
import json
import logging
import time
from urllib.parse import quote, urlparse
from urllib.parse import quote
import webauthn
from django.conf import settings
@@ -54,7 +54,6 @@ from django.utils.http import url_has_allowed_host_and_scheme
from django.utils.translation import gettext_lazy as _
from django.views.generic import TemplateView
from django_otp import match_token
from webauthn.helpers import generate_challenge
from pretix.base.auth import get_auth_backends
from pretix.base.forms.auth import (
@@ -63,6 +62,7 @@ from pretix.base.forms.auth import (
from pretix.base.models import TeamInvite, U2FDevice, User, WebAuthnDevice
from pretix.base.services.mail import SendMailException
from pretix.helpers.http import redirect_to_url
from pretix.helpers.webauthn import generate_challenge
logger = logging.getLogger(__name__)
@@ -389,10 +389,6 @@ def get_u2f_appid(request):
return settings.SITE_URL
def get_webauthn_rp_id(request):
return urlparse(settings.SITE_URL).hostname
class Login2FAView(TemplateView):
template_name = 'pretixcontrol/auth/login_2fa.html'
@@ -431,41 +427,25 @@ class Login2FAView(TemplateView):
devices = U2FDevice.objects.filter(user=self.user)
for d in devices:
credential_current_sign_count = d.sign_count if isinstance(d, WebAuthnDevice) else 0
try:
webauthn_assertion_response = webauthn.verify_authentication_response(
credential=resp,
expected_challenge=base64.b64decode(challenge),
expected_rp_id=get_webauthn_rp_id(self.request),
expected_origin=settings.SITE_URL,
credential_public_key=d.webauthnpubkey,
credential_current_sign_count=credential_current_sign_count,
)
sign_count = webauthn_assertion_response.new_sign_count
if sign_count < credential_current_sign_count:
raise Exception("Possible replay attack, sign count not higher")
except Exception:
wu = d.webauthnuser
if isinstance(d, U2FDevice):
# https://www.w3.org/TR/webauthn/#sctn-appid-extension says
# "When verifying the assertion, expect that the rpIdHash MAY be the hash of the AppID instead of the RP ID."
try:
webauthn_assertion_response = webauthn.verify_authentication_response(
credential=resp,
expected_challenge=base64.b64decode(challenge),
expected_rp_id=get_u2f_appid(self.request),
expected_origin=settings.SITE_URL,
credential_public_key=d.webauthnpubkey,
credential_current_sign_count=credential_current_sign_count,
)
if webauthn_assertion_response.new_sign_count < 1:
raise Exception("Possible replay attack, sign count set")
except Exception:
logger.exception('U2F login failed')
else:
valid = True
break
else:
logger.exception('Webauthn login failed')
# RP_ID needs to be appId for U2F devices, but we can't
# set it that way in U2FDevice.webauthnuser, since that
# breaks the frontend part.
wu.rp_id = settings.SITE_URL
webauthn_assertion_response = webauthn.WebAuthnAssertionResponse(
wu,
resp,
challenge,
settings.SITE_URL,
uv_required=False # User Verification
)
sign_count = webauthn_assertion_response.verify()
except Exception:
logger.exception('U2F login failed')
else:
if isinstance(d, WebAuthnDevice):
d.sign_count = sign_count
@@ -491,24 +471,23 @@ class Login2FAView(TemplateView):
ctx = super().get_context_data()
if 'webauthn_challenge' in self.request.session:
del self.request.session['webauthn_challenge']
challenge = generate_challenge()
self.request.session['webauthn_challenge'] = base64.b64encode(challenge).decode()
challenge = generate_challenge(32)
self.request.session['webauthn_challenge'] = challenge
devices = [
device.webauthndevice for device in WebAuthnDevice.objects.filter(confirmed=True, user=self.user)
device.webauthnuser for device in WebAuthnDevice.objects.filter(confirmed=True, user=self.user)
] + [
device.webauthndevice for device in U2FDevice.objects.filter(confirmed=True, user=self.user)
device.webauthnuser for device in U2FDevice.objects.filter(confirmed=True, user=self.user)
]
if devices:
auth_options = webauthn.generate_authentication_options(
rp_id=get_webauthn_rp_id(self.request),
challenge=challenge,
allow_credentials=devices,
webauthn_assertion_options = webauthn.WebAuthnAssertionOptions(
devices,
challenge
)
# Backwards compatibility to U2F
j = json.loads(webauthn.options_to_json(auth_options))
j["extensions"] = {"appid": get_u2f_appid(self.request)}
ctx['jsondata'] = json.dumps(j)
ad = webauthn_assertion_options.assertion_dict
ad['extensions'] = {
'appid': get_u2f_appid(self.request)
}
ctx['jsondata'] = json.dumps(ad)
return ctx
def get(self, request, *args, **kwargs):

View File

@@ -262,7 +262,7 @@ class BaseEditorView(EventPermissionRequiredMixin, TemplateView):
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['fonts'] = get_fonts()
ctx['fonts'] = get_fonts(self.request.event, pdf_support_required=True)
ctx['pdf'] = self.get_current_background()
ctx['variables'] = self.get_variables()
ctx['images'] = self.get_images()
@@ -278,7 +278,7 @@ class FontsCSSView(TemplateView):
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['fonts'] = get_fonts()
ctx['fonts'] = get_fonts(self.request.event if hasattr(self.request, 'event') else None, pdf_support_required=True)
return ctx

View File

@@ -674,7 +674,6 @@ def itemvar_select2(request, **kwargs):
{
'id': k,
'text': str(v),
'inactive': d,
}
for k, v, d in choices
],
@@ -716,7 +715,6 @@ def itemvars_select2(request, **kwargs):
{
'id': k,
'text': str(v),
'inactive': d,
}
for k, v, d in choices
],

View File

@@ -35,9 +35,10 @@
import base64
import json
import logging
import os
import time
from collections import defaultdict
from urllib.parse import quote
from urllib.parse import quote, urlparse
import webauthn
from django.conf import settings
@@ -56,7 +57,6 @@ from django.views.generic import FormView, ListView, TemplateView, UpdateView
from django_otp.plugins.otp_static.models import StaticDevice
from django_otp.plugins.otp_totp.models import TOTPDevice
from django_scopes import scopes_disabled
from webauthn.helpers import generate_challenge, generate_user_handle
from pretix.base.auth import get_auth_backends
from pretix.base.forms.auth import ReauthForm
@@ -70,9 +70,9 @@ from pretix.control.forms.users import StaffSessionForm
from pretix.control.permissions import (
AdministratorPermissionRequiredMixin, StaffMemberRequiredMixin,
)
from pretix.control.views.auth import get_u2f_appid, get_webauthn_rp_id
from pretix.control.views.auth import get_u2f_appid
from pretix.helpers.http import redirect_to_url
from pretix.helpers.u2f import websafe_encode
from pretix.helpers.webauthn import generate_challenge, generate_ukey
REAL_DEVICE_TYPES = (TOTPDevice, WebAuthnDevice, U2FDevice)
logger = logging.getLogger(__name__)
@@ -105,41 +105,25 @@ class ReauthView(TemplateView):
devices = U2FDevice.objects.filter(user=self.request.user)
for d in devices:
credential_current_sign_count = d.sign_count if isinstance(d, WebAuthnDevice) else 0
try:
webauthn_assertion_response = webauthn.verify_authentication_response(
credential=resp,
expected_challenge=base64.b64decode(challenge),
expected_rp_id=get_webauthn_rp_id(self.request),
expected_origin=settings.SITE_URL,
credential_public_key=d.webauthnpubkey,
credential_current_sign_count=credential_current_sign_count,
)
sign_count = webauthn_assertion_response.new_sign_count
if sign_count < credential_current_sign_count:
raise Exception("Possible replay attack, sign count not higher")
except Exception:
wu = d.webauthnuser
if isinstance(d, U2FDevice):
# https://www.w3.org/TR/webauthn/#sctn-appid-extension says
# "When verifying the assertion, expect that the rpIdHash MAY be the hash of the AppID instead of the RP ID."
try:
webauthn_assertion_response = webauthn.verify_authentication_response(
credential=resp,
expected_challenge=base64.b64decode(challenge),
expected_rp_id=get_u2f_appid(self.request),
expected_origin=settings.SITE_URL,
credential_public_key=d.webauthnpubkey,
credential_current_sign_count=credential_current_sign_count,
)
if webauthn_assertion_response.new_sign_count < 1:
raise Exception("Possible replay attack, sign count set")
except Exception:
logger.exception('U2F login failed')
else:
valid = True
break
else:
logger.exception('Webauthn login failed')
# RP_ID needs to be appId for U2F devices, but we can't
# set it that way in U2FDevice.webauthnuser, since that
# breaks the frontend part.
wu.rp_id = settings.SITE_URL
webauthn_assertion_response = webauthn.WebAuthnAssertionResponse(
wu,
resp,
challenge,
settings.SITE_URL,
uv_required=False # User Verification
)
sign_count = webauthn_assertion_response.verify()
except Exception:
logger.exception('U2F login failed')
else:
if isinstance(d, WebAuthnDevice):
d.sign_count = sign_count
@@ -178,24 +162,23 @@ class ReauthView(TemplateView):
ctx = super().get_context_data()
if 'webauthn_challenge' in self.request.session:
del self.request.session['webauthn_challenge']
challenge = generate_challenge()
self.request.session['webauthn_challenge'] = base64.b64encode(challenge).decode()
challenge = generate_challenge(32)
self.request.session['webauthn_challenge'] = challenge
devices = [
device.webauthndevice for device in WebAuthnDevice.objects.filter(confirmed=True, user=self.request.user)
device.webauthnuser for device in WebAuthnDevice.objects.filter(confirmed=True, user=self.request.user)
] + [
device.webauthndevice for device in U2FDevice.objects.filter(confirmed=True, user=self.request.user)
device.webauthnuser for device in U2FDevice.objects.filter(confirmed=True, user=self.request.user)
]
if devices:
auth_options = webauthn.generate_authentication_options(
rp_id=get_webauthn_rp_id(self.request),
challenge=challenge,
allow_credentials=devices,
webauthn_assertion_options = webauthn.WebAuthnAssertionOptions(
devices,
challenge
)
# Backwards compatibility to U2F
j = json.loads(webauthn.options_to_json(auth_options))
j["extensions"] = {"appid": get_u2f_appid(self.request)}
ctx['jsondata'] = json.dumps(j)
ad = webauthn_assertion_options.assertion_dict
ad['extensions'] = {
'appid': get_u2f_appid(self.request)
}
ctx['jsondata'] = json.dumps(ad)
ctx['form'] = self.form
return ctx
@@ -404,26 +387,23 @@ class User2FADeviceConfirmWebAuthnView(RecentAuthenticationRequiredMixin, Templa
if 'webauthn_challenge' in self.request.session:
del self.request.session['webauthn_challenge']
challenge = generate_challenge()
ukey = generate_user_handle()
challenge = generate_challenge(32)
ukey = generate_ukey()
self.request.session['webauthn_challenge'] = base64.b64encode(challenge).decode()
self.request.session['webauthn_register_ukey'] = base64.b64encode(ukey).decode()
self.request.session['webauthn_challenge'] = challenge
self.request.session['webauthn_register_ukey'] = ukey
devices = [
device.webauthndevice for device in WebAuthnDevice.objects.filter(confirmed=True, user=self.request.user)
] + [
device.webauthndevice for device in U2FDevice.objects.filter(confirmed=True, user=self.request.user)
]
make_credential_options = webauthn.generate_registration_options(
rp_id=get_webauthn_rp_id(self.request),
rp_name=get_webauthn_rp_id(self.request),
user_id=ukey,
user_name=self.request.user.email,
challenge=challenge,
exclude_credentials=devices,
make_credential_options = webauthn.WebAuthnMakeCredentialOptions(
challenge,
urlparse(settings.SITE_URL).netloc,
urlparse(settings.SITE_URL).netloc,
ukey,
self.request.user.email,
str(self.request.user),
settings.SITE_URL,
attestation="none"
)
ctx['jsondata'] = webauthn.options_to_json(make_credential_options)
ctx['jsondata'] = json.dumps(make_credential_options.registration_dict)
return ctx
@@ -432,13 +412,30 @@ class User2FADeviceConfirmWebAuthnView(RecentAuthenticationRequiredMixin, Templa
challenge = self.request.session['webauthn_challenge']
ukey = self.request.session['webauthn_register_ukey']
resp = json.loads(self.request.POST.get("token"))
trust_anchor_dir = os.path.normpath(os.path.join(
os.path.dirname(os.path.abspath(__file__)),
'../../static/webauthn_trusted_attestation_roots' # currently does not exist
))
# We currently do not check attestation certificates, since there's no real risk
# and we do not have any policies specifying what devices can be used. (Also, we
# didn't get it to work.)
# Read more: https://fidoalliance.org/fido-technotes-the-truth-about-attestation/
trusted_attestation_cert_required = False
self_attestation_permitted = True
none_attestation_permitted = True
registration_verification = webauthn.verify_registration_response(
credential=resp,
expected_challenge=base64.b64decode(challenge),
expected_rp_id=get_webauthn_rp_id(self.request),
expected_origin=settings.SITE_URL,
webauthn_registration_response = webauthn.WebAuthnRegistrationResponse(
urlparse(settings.SITE_URL).netloc,
settings.SITE_URL,
resp,
challenge,
trust_anchor_dir,
trusted_attestation_cert_required,
self_attestation_permitted,
none_attestation_permitted,
uv_required=False
)
webauthn_credential = webauthn_registration_response.verify()
# Check that the credentialId is not yet registered to any other user.
# If registration is requested for a credential that is already registered
@@ -446,7 +443,7 @@ class User2FADeviceConfirmWebAuthnView(RecentAuthenticationRequiredMixin, Templa
# ceremony, or it MAY decide to accept the registration, e.g. while deleting
# the older registration.
credential_id_exists = WebAuthnDevice.objects.filter(
credential_id=registration_verification.credential_id
credential_id=webauthn_credential.credential_id
).first()
if credential_id_exists:
messages.error(request, _('This security device is already registered.'))
@@ -454,11 +451,14 @@ class User2FADeviceConfirmWebAuthnView(RecentAuthenticationRequiredMixin, Templa
'device': self.device.pk
}))
self.device.credential_id = websafe_encode(registration_verification.credential_id)
self.device.ukey = websafe_encode(ukey)
self.device.pub_key = websafe_encode(registration_verification.credential_public_key)
self.device.sign_count = registration_verification.sign_count
self.device.rp_id = get_webauthn_rp_id(request)
webauthn_credential.credential_id = str(webauthn_credential.credential_id, "utf-8")
webauthn_credential.public_key = str(webauthn_credential.public_key, "utf-8")
self.device.credential_id = webauthn_credential.credential_id
self.device.ukey = ukey
self.device.pub_key = webauthn_credential.public_key
self.device.sign_count = webauthn_credential.sign_count
self.device.rp_id = urlparse(settings.SITE_URL).netloc
self.device.icon_url = settings.SITE_URL
self.device.confirmed = True
self.device.save()

View File

@@ -32,14 +32,9 @@ def clean_filename(fname):
"Terms.pdf""Terms.pdf.OybgvyAH.22c0583727d5bc.pdf"
This function reverses this operation (leaving names without doubled extension as-is):
This function reverses this operation:
"Terms.pdf.OybgvyAH.22c0583727d5bc.pdf""Terms.pdf"
"Terms.pdf""Terms.pdf"
"""
ext = '.' + fname.split('.')[-1]
parts = fname.rsplit(ext + ".", 1)
if len(parts) == 1:
return parts[0]
else:
return parts[0] + ext
return fname.rsplit(ext + ".", 1)[0] + ext

View File

@@ -44,12 +44,11 @@ def validate_uploaded_file_for_valid_image(f):
# have to read the data into memory.
if hasattr(f, 'temporary_file_path'):
file = f.temporary_file_path()
elif hasattr(f, 'read'):
if hasattr(f, 'seek') and callable(f.seek):
f.seek(0)
file = BytesIO(f.read())
else:
file = BytesIO(f['content'])
if hasattr(f, 'read'):
file = BytesIO(f.read())
else:
file = BytesIO(f['content'])
try:
try:

View File

@@ -62,7 +62,7 @@ def soft_equals(a, b):
def hard_equals(a, b):
"""Implements the '===' operator."""
if type(a) is not type(b):
if type(a) != type(b):
return False
return a == b

View File

@@ -0,0 +1,45 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
import random
import string
def generate_challenge(challenge_len):
return ''.join([
random.SystemRandom().choice(string.ascii_letters + string.digits)
for i in range(challenge_len)
])
def generate_ukey():
"""
Its value's id member is required, and contains an identifier
for the account, specified by the Relying Party. This is not meant
to be displayed to the user, but is used by the Relying Party to
control the number of credentials - an authenticator will never
contain more than one credential for a given Relying Party under
the same id.
A unique identifier for the entity. For a relying party entity,
sets the RP ID. For a user account entity, this will be an
arbitrary string specified by the relying party.
"""
return generate_challenge(20)

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-02-28 13:11+0000\n"
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -609,23 +609,23 @@ msgstr ""
msgid "Selected only"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:791
#: pretix/static/pretixcontrol/js/ui/main.js:784
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:794
#: pretix/static/pretixcontrol/js/ui/main.js:787
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:952
#: pretix/static/pretixcontrol/js/ui/main.js:945
msgid "Use a different name internally"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:992
#: pretix/static/pretixcontrol/js/ui/main.js:985
msgid "Click to close"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1067
#: pretix/static/pretixcontrol/js/ui/main.js:1060
msgid "You have unsaved changes!"
msgstr ""

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-02-28 13:11+0000\n"
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
"PO-Revision-Date: 2021-09-15 11:22+0000\n"
"Last-Translator: Mohamed Tawfiq <mtawfiq@wafyapp.com>\n"
"Language-Team: Arabic <https://translate.pretix.eu/projects/pretix/pretix-js/"
@@ -639,23 +639,23 @@ msgstr "لا شيء"
msgid "Selected only"
msgstr "المختارة فقط"
#: pretix/static/pretixcontrol/js/ui/main.js:791
#: pretix/static/pretixcontrol/js/ui/main.js:784
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:794
#: pretix/static/pretixcontrol/js/ui/main.js:787
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:952
#: pretix/static/pretixcontrol/js/ui/main.js:945
msgid "Use a different name internally"
msgstr "قم باستخدم اسم مختلف داخليا"
#: pretix/static/pretixcontrol/js/ui/main.js:992
#: pretix/static/pretixcontrol/js/ui/main.js:985
msgid "Click to close"
msgstr "اضغط لاغلاق الصفحة"
#: pretix/static/pretixcontrol/js/ui/main.js:1067
#: pretix/static/pretixcontrol/js/ui/main.js:1060
msgid "You have unsaved changes!"
msgstr "لم تقم بحفظ التعديلات!"

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-02-28 13:11+0000\n"
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
"PO-Revision-Date: 2020-12-19 07:00+0000\n"
"Last-Translator: albert <albert.serra.monner@gmail.com>\n"
"Language-Team: Catalan <https://translate.pretix.eu/projects/pretix/pretix-"
@@ -610,23 +610,23 @@ msgstr ""
msgid "Selected only"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:791
#: pretix/static/pretixcontrol/js/ui/main.js:784
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:794
#: pretix/static/pretixcontrol/js/ui/main.js:787
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:952
#: pretix/static/pretixcontrol/js/ui/main.js:945
msgid "Use a different name internally"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:992
#: pretix/static/pretixcontrol/js/ui/main.js:985
msgid "Click to close"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1067
#: pretix/static/pretixcontrol/js/ui/main.js:1060
msgid "You have unsaved changes!"
msgstr ""

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-02-28 13:11+0000\n"
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
"PO-Revision-Date: 2023-09-15 06:00+0000\n"
"Last-Translator: Michael <michael.happl@gmx.at>\n"
"Language-Team: Czech <https://translate.pretix.eu/projects/pretix/pretix-js/"
@@ -633,23 +633,23 @@ msgstr "Žádný"
msgid "Selected only"
msgstr "Pouze vybrané"
#: pretix/static/pretixcontrol/js/ui/main.js:791
#: pretix/static/pretixcontrol/js/ui/main.js:784
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:794
#: pretix/static/pretixcontrol/js/ui/main.js:787
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:952
#: pretix/static/pretixcontrol/js/ui/main.js:945
msgid "Use a different name internally"
msgstr "Interně používat jiný název"
#: pretix/static/pretixcontrol/js/ui/main.js:992
#: pretix/static/pretixcontrol/js/ui/main.js:985
msgid "Click to close"
msgstr "Kliknutím zavřete"
#: pretix/static/pretixcontrol/js/ui/main.js:1067
#: pretix/static/pretixcontrol/js/ui/main.js:1060
msgid "You have unsaved changes!"
msgstr "Máte neuložené změny!"

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-02-28 13:11+0000\n"
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -610,23 +610,23 @@ msgstr ""
msgid "Selected only"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:791
#: pretix/static/pretixcontrol/js/ui/main.js:784
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:794
#: pretix/static/pretixcontrol/js/ui/main.js:787
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:952
#: pretix/static/pretixcontrol/js/ui/main.js:945
msgid "Use a different name internally"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:992
#: pretix/static/pretixcontrol/js/ui/main.js:985
msgid "Click to close"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1067
#: pretix/static/pretixcontrol/js/ui/main.js:1060
msgid "You have unsaved changes!"
msgstr ""

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-02-28 13:11+0000\n"
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
"PO-Revision-Date: 2022-12-01 17:00+0000\n"
"Last-Translator: Mie Frydensbjerg <mif@aarhus.dk>\n"
"Language-Team: Danish <https://translate.pretix.eu/projects/pretix/pretix-js/"
@@ -653,23 +653,23 @@ msgstr "Ingen"
msgid "Selected only"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:791
#: pretix/static/pretixcontrol/js/ui/main.js:784
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:794
#: pretix/static/pretixcontrol/js/ui/main.js:787
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:952
#: pretix/static/pretixcontrol/js/ui/main.js:945
msgid "Use a different name internally"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:992
#: pretix/static/pretixcontrol/js/ui/main.js:985
msgid "Click to close"
msgstr "Klik for at lukke"
#: pretix/static/pretixcontrol/js/ui/main.js:1067
#: pretix/static/pretixcontrol/js/ui/main.js:1060
msgid "You have unsaved changes!"
msgstr "Du har ændringer, der ikke er gemt!"

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-02-28 13:11+0000\n"
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
"PO-Revision-Date: 2024-02-10 00:00+0000\n"
"Last-Translator: Phin Wolkwitz <wolkwitz@rami.io>\n"
"Language-Team: German <https://translate.pretix.eu/projects/pretix/pretix-js/"
@@ -635,23 +635,23 @@ msgstr "Keine"
msgid "Selected only"
msgstr "Nur ausgewählte"
#: pretix/static/pretixcontrol/js/ui/main.js:791
#: pretix/static/pretixcontrol/js/ui/main.js:784
msgid "Enter page number between 1 and %(max)s."
msgstr "Geben Sie eine Seitenzahl zwischen 1 und %(max)s ein."
#: pretix/static/pretixcontrol/js/ui/main.js:794
#: pretix/static/pretixcontrol/js/ui/main.js:787
msgid "Invalid page number."
msgstr "Ungültige Seitenzahl."
#: pretix/static/pretixcontrol/js/ui/main.js:952
#: pretix/static/pretixcontrol/js/ui/main.js:945
msgid "Use a different name internally"
msgstr "Intern einen anderen Namen verwenden"
#: pretix/static/pretixcontrol/js/ui/main.js:992
#: pretix/static/pretixcontrol/js/ui/main.js:985
msgid "Click to close"
msgstr "Klicken zum Schließen"
#: pretix/static/pretixcontrol/js/ui/main.js:1067
#: pretix/static/pretixcontrol/js/ui/main.js:1060
msgid "You have unsaved changes!"
msgstr "Sie haben ungespeicherte Änderungen!"

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-02-28 13:11+0000\n"
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
"PO-Revision-Date: 2024-02-10 00:00+0000\n"
"Last-Translator: Phin Wolkwitz <wolkwitz@rami.io>\n"
"Language-Team: German (informal) <https://translate.pretix.eu/projects/"
@@ -634,23 +634,23 @@ msgstr "Keine"
msgid "Selected only"
msgstr "Nur ausgewählte"
#: pretix/static/pretixcontrol/js/ui/main.js:791
#: pretix/static/pretixcontrol/js/ui/main.js:784
msgid "Enter page number between 1 and %(max)s."
msgstr "Gib eine Seitenzahl zwischen 1 und %(max)s ein."
#: pretix/static/pretixcontrol/js/ui/main.js:794
#: pretix/static/pretixcontrol/js/ui/main.js:787
msgid "Invalid page number."
msgstr "Ungültige Seitenzahl."
#: pretix/static/pretixcontrol/js/ui/main.js:952
#: pretix/static/pretixcontrol/js/ui/main.js:945
msgid "Use a different name internally"
msgstr "Intern einen anderen Namen verwenden"
#: pretix/static/pretixcontrol/js/ui/main.js:992
#: pretix/static/pretixcontrol/js/ui/main.js:985
msgid "Click to close"
msgstr "Klicken zum Schließen"
#: pretix/static/pretixcontrol/js/ui/main.js:1067
#: pretix/static/pretixcontrol/js/ui/main.js:1060
msgid "You have unsaved changes!"
msgstr "Du hast ungespeicherte Änderungen!"

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-02-28 13:11+0000\n"
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -609,23 +609,23 @@ msgstr ""
msgid "Selected only"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:791
#: pretix/static/pretixcontrol/js/ui/main.js:784
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:794
#: pretix/static/pretixcontrol/js/ui/main.js:787
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:952
#: pretix/static/pretixcontrol/js/ui/main.js:945
msgid "Use a different name internally"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:992
#: pretix/static/pretixcontrol/js/ui/main.js:985
msgid "Click to close"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1067
#: pretix/static/pretixcontrol/js/ui/main.js:1060
msgid "You have unsaved changes!"
msgstr ""

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-02-28 13:11+0000\n"
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
"PO-Revision-Date: 2019-10-03 19:00+0000\n"
"Last-Translator: Chris Spy <chrispiropoulou@hotmail.com>\n"
"Language-Team: Greek <https://translate.pretix.eu/projects/pretix/pretix-js/"
@@ -669,23 +669,23 @@ msgstr "Κανένας"
msgid "Selected only"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:791
#: pretix/static/pretixcontrol/js/ui/main.js:784
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:794
#: pretix/static/pretixcontrol/js/ui/main.js:787
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:952
#: pretix/static/pretixcontrol/js/ui/main.js:945
msgid "Use a different name internally"
msgstr "Χρησιμοποιήστε διαφορετικό όνομα εσωτερικά"
#: pretix/static/pretixcontrol/js/ui/main.js:992
#: pretix/static/pretixcontrol/js/ui/main.js:985
msgid "Click to close"
msgstr "Κάντε κλικ για να κλείσετε"
#: pretix/static/pretixcontrol/js/ui/main.js:1067
#: pretix/static/pretixcontrol/js/ui/main.js:1060
msgid "You have unsaved changes!"
msgstr ""

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-02-28 13:11+0000\n"
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -609,23 +609,23 @@ msgstr ""
msgid "Selected only"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:791
#: pretix/static/pretixcontrol/js/ui/main.js:784
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:794
#: pretix/static/pretixcontrol/js/ui/main.js:787
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:952
#: pretix/static/pretixcontrol/js/ui/main.js:945
msgid "Use a different name internally"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:992
#: pretix/static/pretixcontrol/js/ui/main.js:985
msgid "Click to close"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1067
#: pretix/static/pretixcontrol/js/ui/main.js:1060
msgid "You have unsaved changes!"
msgstr ""

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-02-28 13:11+0000\n"
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
"PO-Revision-Date: 2023-11-07 14:00+0000\n"
"Last-Translator: Zona Vip <contacto@zonavip.mx>\n"
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/pretix-"
@@ -639,23 +639,23 @@ msgstr "Ninguno"
msgid "Selected only"
msgstr "Solamente seleccionados"
#: pretix/static/pretixcontrol/js/ui/main.js:791
#: pretix/static/pretixcontrol/js/ui/main.js:784
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:794
#: pretix/static/pretixcontrol/js/ui/main.js:787
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:952
#: pretix/static/pretixcontrol/js/ui/main.js:945
msgid "Use a different name internally"
msgstr "Usar un nombre diferente internamente"
#: pretix/static/pretixcontrol/js/ui/main.js:992
#: pretix/static/pretixcontrol/js/ui/main.js:985
msgid "Click to close"
msgstr "Click para cerrar"
#: pretix/static/pretixcontrol/js/ui/main.js:1067
#: pretix/static/pretixcontrol/js/ui/main.js:1060
msgid "You have unsaved changes!"
msgstr "¡Tienes cambios sin guardar!"

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-02-28 13:11+0000\n"
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
"PO-Revision-Date: 2021-11-10 05:00+0000\n"
"Last-Translator: Jaakko Rinta-Filppula <jaakko@r-f.fi>\n"
"Language-Team: Finnish <https://translate.pretix.eu/projects/pretix/pretix-"
@@ -634,23 +634,23 @@ msgstr ""
msgid "Selected only"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:791
#: pretix/static/pretixcontrol/js/ui/main.js:784
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:794
#: pretix/static/pretixcontrol/js/ui/main.js:787
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:952
#: pretix/static/pretixcontrol/js/ui/main.js:945
msgid "Use a different name internally"
msgstr "Käytä toista nimeä sisäisesti"
#: pretix/static/pretixcontrol/js/ui/main.js:992
#: pretix/static/pretixcontrol/js/ui/main.js:985
msgid "Click to close"
msgstr "Sulje klikkaamalla"
#: pretix/static/pretixcontrol/js/ui/main.js:1067
#: pretix/static/pretixcontrol/js/ui/main.js:1060
msgid "You have unsaved changes!"
msgstr "Sinulla on tallentamattomia muutoksia!"

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: French\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-02-28 13:11+0000\n"
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
"PO-Revision-Date: 2023-09-11 10:00+0000\n"
"Last-Translator: Ronan LE MEILLAT <ronan.le_meillat@highcanfly.club>\n"
"Language-Team: French <https://translate.pretix.eu/projects/pretix/pretix-js/"
@@ -638,23 +638,23 @@ msgstr "Aucun"
msgid "Selected only"
msgstr "Seuls les sélectionnés"
#: pretix/static/pretixcontrol/js/ui/main.js:791
#: pretix/static/pretixcontrol/js/ui/main.js:784
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:794
#: pretix/static/pretixcontrol/js/ui/main.js:787
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:952
#: pretix/static/pretixcontrol/js/ui/main.js:945
msgid "Use a different name internally"
msgstr "Utiliser un nom différent en interne"
#: pretix/static/pretixcontrol/js/ui/main.js:992
#: pretix/static/pretixcontrol/js/ui/main.js:985
msgid "Click to close"
msgstr "Cliquez pour fermer"
#: pretix/static/pretixcontrol/js/ui/main.js:1067
#: pretix/static/pretixcontrol/js/ui/main.js:1060
msgid "You have unsaved changes!"
msgstr "Vous avez des modifications non sauvegardées !"

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-02-28 13:11+0000\n"
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
"PO-Revision-Date: 2022-02-22 22:00+0000\n"
"Last-Translator: Ismael Menéndez Fernández <ismael.menendez@balidea.com>\n"
"Language-Team: Galician <https://translate.pretix.eu/projects/pretix/pretix-"
@@ -641,23 +641,23 @@ msgstr "Ningún"
msgid "Selected only"
msgstr "Soamente seleccionados"
#: pretix/static/pretixcontrol/js/ui/main.js:791
#: pretix/static/pretixcontrol/js/ui/main.js:784
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:794
#: pretix/static/pretixcontrol/js/ui/main.js:787
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:952
#: pretix/static/pretixcontrol/js/ui/main.js:945
msgid "Use a different name internally"
msgstr "Usar un nome diferente internamente"
#: pretix/static/pretixcontrol/js/ui/main.js:992
#: pretix/static/pretixcontrol/js/ui/main.js:985
msgid "Click to close"
msgstr "Click para cerrar"
#: pretix/static/pretixcontrol/js/ui/main.js:1067
#: pretix/static/pretixcontrol/js/ui/main.js:1060
msgid "You have unsaved changes!"
msgstr "Tes cambios sen gardar!"

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-02-28 13:11+0000\n"
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
"PO-Revision-Date: 2021-09-24 13:54+0000\n"
"Last-Translator: ofirtro <ofir.tro@gmail.com>\n"
"Language-Team: Hebrew <https://translate.pretix.eu/projects/pretix/pretix-js/"
@@ -617,23 +617,23 @@ msgstr ""
msgid "Selected only"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:791
#: pretix/static/pretixcontrol/js/ui/main.js:784
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:794
#: pretix/static/pretixcontrol/js/ui/main.js:787
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:952
#: pretix/static/pretixcontrol/js/ui/main.js:945
msgid "Use a different name internally"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:992
#: pretix/static/pretixcontrol/js/ui/main.js:985
msgid "Click to close"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1067
#: pretix/static/pretixcontrol/js/ui/main.js:1060
msgid "You have unsaved changes!"
msgstr ""

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-02-28 13:11+0000\n"
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
"PO-Revision-Date: 2024-01-31 04:00+0000\n"
"Last-Translator: Pavle Ergović <pavleergovic@gmail.com>\n"
"Language-Team: Croatian <https://translate.pretix.eu/projects/pretix/pretix-"
@@ -611,23 +611,23 @@ msgstr ""
msgid "Selected only"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:791
#: pretix/static/pretixcontrol/js/ui/main.js:784
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:794
#: pretix/static/pretixcontrol/js/ui/main.js:787
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:952
#: pretix/static/pretixcontrol/js/ui/main.js:945
msgid "Use a different name internally"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:992
#: pretix/static/pretixcontrol/js/ui/main.js:985
msgid "Click to close"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1067
#: pretix/static/pretixcontrol/js/ui/main.js:1060
msgid "You have unsaved changes!"
msgstr ""

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-02-28 13:11+0000\n"
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
"PO-Revision-Date: 2020-01-24 08:00+0000\n"
"Last-Translator: Prokaj Miklós <mixolid0@gmail.com>\n"
"Language-Team: Hungarian <https://translate.pretix.eu/projects/pretix/pretix-"
@@ -657,23 +657,23 @@ msgstr "Semmi"
msgid "Selected only"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:791
#: pretix/static/pretixcontrol/js/ui/main.js:784
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:794
#: pretix/static/pretixcontrol/js/ui/main.js:787
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:952
#: pretix/static/pretixcontrol/js/ui/main.js:945
msgid "Use a different name internally"
msgstr "Használj másik nevet"
#: pretix/static/pretixcontrol/js/ui/main.js:992
#: pretix/static/pretixcontrol/js/ui/main.js:985
msgid "Click to close"
msgstr "Bezárásért kattints"
#: pretix/static/pretixcontrol/js/ui/main.js:1067
#: pretix/static/pretixcontrol/js/ui/main.js:1060
msgid "You have unsaved changes!"
msgstr "Mentetlen változtatások!"

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-02-28 13:11+0000\n"
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
"PO-Revision-Date: 2023-09-20 14:01+0000\n"
"Last-Translator: Mahdia Aliyy <mahdlyy.k@gmail.com>\n"
"Language-Team: Indonesian <https://translate.pretix.eu/projects/pretix/"
@@ -640,23 +640,23 @@ msgstr "Tidak ada"
msgid "Selected only"
msgstr "Hanya dipilih"
#: pretix/static/pretixcontrol/js/ui/main.js:791
#: pretix/static/pretixcontrol/js/ui/main.js:784
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:794
#: pretix/static/pretixcontrol/js/ui/main.js:787
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:952
#: pretix/static/pretixcontrol/js/ui/main.js:945
msgid "Use a different name internally"
msgstr "Gunakan nama yang berbeda secara internal"
#: pretix/static/pretixcontrol/js/ui/main.js:992
#: pretix/static/pretixcontrol/js/ui/main.js:985
msgid "Click to close"
msgstr "Klik untuk menutup"
#: pretix/static/pretixcontrol/js/ui/main.js:1067
#: pretix/static/pretixcontrol/js/ui/main.js:1060
msgid "You have unsaved changes!"
msgstr "Anda memiliki perubahan yang belum disimpan!"

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-02-28 13:11+0000\n"
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
"PO-Revision-Date: 2023-05-18 01:00+0000\n"
"Last-Translator: M C <micasadmail@gmail.com>\n"
"Language-Team: Italian <https://translate.pretix.eu/projects/pretix/pretix-"
@@ -635,23 +635,23 @@ msgstr "Nessuno"
msgid "Selected only"
msgstr "Solo i selezionati"
#: pretix/static/pretixcontrol/js/ui/main.js:791
#: pretix/static/pretixcontrol/js/ui/main.js:784
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:794
#: pretix/static/pretixcontrol/js/ui/main.js:787
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:952
#: pretix/static/pretixcontrol/js/ui/main.js:945
msgid "Use a different name internally"
msgstr "Utilizza un nome diverso internamente"
#: pretix/static/pretixcontrol/js/ui/main.js:992
#: pretix/static/pretixcontrol/js/ui/main.js:985
msgid "Click to close"
msgstr "Clicca per chiudere"
#: pretix/static/pretixcontrol/js/ui/main.js:1067
#: pretix/static/pretixcontrol/js/ui/main.js:1060
msgid "You have unsaved changes!"
msgstr "Hai cambiamenti non salvati!"

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-02-28 13:11+0000\n"
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
"PO-Revision-Date: 2022-03-15 00:00+0000\n"
"Last-Translator: Yuriko Matsunami <y.matsunami@enobyte.com>\n"
"Language-Team: Japanese <https://translate.pretix.eu/projects/pretix/pretix-"
@@ -637,23 +637,23 @@ msgstr "ない"
msgid "Selected only"
msgstr "選択したもののみ"
#: pretix/static/pretixcontrol/js/ui/main.js:791
#: pretix/static/pretixcontrol/js/ui/main.js:784
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:794
#: pretix/static/pretixcontrol/js/ui/main.js:787
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:952
#: pretix/static/pretixcontrol/js/ui/main.js:945
msgid "Use a different name internally"
msgstr "内部で別の名前を使用してください"
#: pretix/static/pretixcontrol/js/ui/main.js:992
#: pretix/static/pretixcontrol/js/ui/main.js:985
msgid "Click to close"
msgstr "クリックして閉じる"
#: pretix/static/pretixcontrol/js/ui/main.js:1067
#: pretix/static/pretixcontrol/js/ui/main.js:1060
msgid "You have unsaved changes!"
msgstr "保存されていない変更があります!"

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-02-28 13:11+0000\n"
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -609,23 +609,23 @@ msgstr ""
msgid "Selected only"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:791
#: pretix/static/pretixcontrol/js/ui/main.js:784
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:794
#: pretix/static/pretixcontrol/js/ui/main.js:787
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:952
#: pretix/static/pretixcontrol/js/ui/main.js:945
msgid "Use a different name internally"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:992
#: pretix/static/pretixcontrol/js/ui/main.js:985
msgid "Click to close"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1067
#: pretix/static/pretixcontrol/js/ui/main.js:1060
msgid "You have unsaved changes!"
msgstr ""

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-02-28 13:11+0000\n"
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -611,23 +611,23 @@ msgstr ""
msgid "Selected only"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:791
#: pretix/static/pretixcontrol/js/ui/main.js:784
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:794
#: pretix/static/pretixcontrol/js/ui/main.js:787
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:952
#: pretix/static/pretixcontrol/js/ui/main.js:945
msgid "Use a different name internally"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:992
#: pretix/static/pretixcontrol/js/ui/main.js:985
msgid "Click to close"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1067
#: pretix/static/pretixcontrol/js/ui/main.js:1060
msgid "You have unsaved changes!"
msgstr ""

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-02-28 13:11+0000\n"
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
"PO-Revision-Date: 2022-04-06 03:00+0000\n"
"Last-Translator: Liga V <lerning_by_dreaming@gmx.de>\n"
"Language-Team: Latvian <https://translate.pretix.eu/projects/pretix/pretix-"
@@ -644,23 +644,23 @@ msgstr "Neviens"
msgid "Selected only"
msgstr "Tikai atzīmētos"
#: pretix/static/pretixcontrol/js/ui/main.js:791
#: pretix/static/pretixcontrol/js/ui/main.js:784
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:794
#: pretix/static/pretixcontrol/js/ui/main.js:787
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:952
#: pretix/static/pretixcontrol/js/ui/main.js:945
msgid "Use a different name internally"
msgstr "Izmantojiet citu nosaukumu iekšēji"
#: pretix/static/pretixcontrol/js/ui/main.js:992
#: pretix/static/pretixcontrol/js/ui/main.js:985
msgid "Click to close"
msgstr "Noklikšķiniet, lai aizvērtu"
#: pretix/static/pretixcontrol/js/ui/main.js:1067
#: pretix/static/pretixcontrol/js/ui/main.js:1060
msgid "You have unsaved changes!"
msgstr "Jums ir nesaglabātas izmaiņas!"

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-02-28 13:11+0000\n"
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -609,23 +609,23 @@ msgstr ""
msgid "Selected only"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:791
#: pretix/static/pretixcontrol/js/ui/main.js:784
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:794
#: pretix/static/pretixcontrol/js/ui/main.js:787
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:952
#: pretix/static/pretixcontrol/js/ui/main.js:945
msgid "Use a different name internally"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:992
#: pretix/static/pretixcontrol/js/ui/main.js:985
msgid "Click to close"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1067
#: pretix/static/pretixcontrol/js/ui/main.js:1060
msgid "You have unsaved changes!"
msgstr ""

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-02-28 13:11+0000\n"
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
"PO-Revision-Date: 2023-11-02 13:02+0000\n"
"Last-Translator: fyksen <fredrik@fyksen.me>\n"
"Language-Team: Norwegian Bokmål <https://translate.pretix.eu/projects/pretix/"
@@ -624,23 +624,23 @@ msgstr "Ingen"
msgid "Selected only"
msgstr "Kun valgte"
#: pretix/static/pretixcontrol/js/ui/main.js:791
#: pretix/static/pretixcontrol/js/ui/main.js:784
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:794
#: pretix/static/pretixcontrol/js/ui/main.js:787
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:952
#: pretix/static/pretixcontrol/js/ui/main.js:945
msgid "Use a different name internally"
msgstr "Bruk et annet navn internt"
#: pretix/static/pretixcontrol/js/ui/main.js:992
#: pretix/static/pretixcontrol/js/ui/main.js:985
msgid "Click to close"
msgstr "Klikk for å lukke"
#: pretix/static/pretixcontrol/js/ui/main.js:1067
#: pretix/static/pretixcontrol/js/ui/main.js:1060
msgid "You have unsaved changes!"
msgstr "Du har ikke-lagrede endringer!"

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More