forked from CGM_Public/pretix_original
Compare commits
78 Commits
event_font
...
back-to-th
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4abfb0bdf | ||
|
|
d32bd717b7 | ||
|
|
6e6b75d55e | ||
|
|
50b5f760bb | ||
|
|
9ab2e61c31 | ||
|
|
4876a0b61f | ||
|
|
56bbcb65c3 | ||
|
|
5bb1cb498f | ||
|
|
6bf23b0fdd | ||
|
|
5deb1a8c69 | ||
|
|
1523137300 | ||
|
|
04ef097eb1 | ||
|
|
a5d4434a64 | ||
|
|
3b3c668153 | ||
|
|
d339d67111 | ||
|
|
a8aee6c824 | ||
|
|
6466987493 | ||
|
|
ff962805cd | ||
|
|
2b5f46164f | ||
|
|
d74451ded1 | ||
|
|
62f0c82d8d | ||
|
|
5b587774bb | ||
|
|
88ea8ee2ea | ||
|
|
56e0ab8378 | ||
|
|
a9ae237b1a | ||
|
|
27823b7bf6 | ||
|
|
4231cd2576 | ||
|
|
6aa5196f18 | ||
|
|
c1eac5e91e | ||
|
|
410e06364a | ||
|
|
ae137f8f16 | ||
|
|
f9f3f9f868 | ||
|
|
395eadde47 | ||
|
|
2be790fa45 | ||
|
|
f9d78eaf1a | ||
|
|
2d5d27e950 | ||
|
|
c6fa19d771 | ||
|
|
3129253eef | ||
|
|
b69ab86458 | ||
|
|
80f7ae0b76 | ||
|
|
160f9a4363 | ||
|
|
24b5b9373d | ||
|
|
178c40aee6 | ||
|
|
49c41878d2 | ||
|
|
fa4c29cf23 | ||
|
|
75b93eebc5 | ||
|
|
5a406abdd6 | ||
|
|
6712baf534 | ||
|
|
4d9243151f | ||
|
|
b89a4f7b32 | ||
|
|
c80d5b1bb2 | ||
|
|
0334c2f433 | ||
|
|
6bc46b7aec | ||
|
|
3ebe622189 | ||
|
|
25fb1ee3be | ||
|
|
a3586a73f1 | ||
|
|
93eb041acc | ||
|
|
63894ca3da | ||
|
|
73b2cce435 | ||
|
|
0a711f4965 | ||
|
|
75bf200aac | ||
|
|
11307de30a | ||
|
|
863db60786 | ||
|
|
f5a1adedca | ||
|
|
ea74688633 | ||
|
|
57738f19bf | ||
|
|
7b5ce5e198 | ||
|
|
d5f9beef69 | ||
|
|
eee39b1300 | ||
|
|
c2fdea020d | ||
|
|
f87e089734 | ||
|
|
0fad7472c0 | ||
|
|
bd0a223066 | ||
|
|
782c1a5d39 | ||
|
|
5c99d3bf69 | ||
|
|
5e6307acc9 | ||
|
|
d4bfa9d773 | ||
|
|
b7f540251c |
@@ -94,7 +94,9 @@ 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`` or ``https://example.org/order/``.
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
@@ -179,6 +179,11 @@ 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``)
|
||||
@@ -367,6 +372,7 @@ 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,
|
||||
@@ -589,6 +595,7 @@ Fetching individual orders
|
||||
"country": "DE",
|
||||
"state": null,
|
||||
"voucher": null,
|
||||
"voucher_budget_use": null,
|
||||
"tax_rate": "0.00",
|
||||
"tax_rule": null,
|
||||
"tax_value": "0.00",
|
||||
@@ -1541,6 +1548,7 @@ 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",
|
||||
@@ -1654,6 +1662,7 @@ Fetching individual positions
|
||||
},
|
||||
"attendee_email": null,
|
||||
"voucher": null,
|
||||
"voucher_budget_use": null,
|
||||
"tax_rate": "0.00",
|
||||
"tax_rule": null,
|
||||
"tax_value": "0.00",
|
||||
|
||||
@@ -84,8 +84,6 @@ convenient to you:
|
||||
|
||||
.. automethod:: _register_fonts
|
||||
|
||||
.. automethod:: _register_event_fonts
|
||||
|
||||
.. automethod:: _on_first_page
|
||||
|
||||
.. automethod:: _on_other_page
|
||||
|
||||
@@ -34,6 +34,7 @@ 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).
|
||||
@@ -109,6 +110,7 @@ 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",
|
||||
@@ -162,6 +164,7 @@ 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",
|
||||
@@ -365,6 +368,7 @@ 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,
|
||||
@@ -394,6 +398,7 @@ 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",
|
||||
@@ -454,6 +459,7 @@ 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",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
sphinx==7.0.*
|
||||
sphinx==7.2.*
|
||||
jinja2==3.1.*
|
||||
sphinx-rtd-theme
|
||||
sphinxcontrib-httpdomain
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
-e ../
|
||||
sphinx==7.0.*
|
||||
sphinx==7.2.*
|
||||
jinja2==3.1.*
|
||||
sphinx-rtd-theme
|
||||
sphinxcontrib-httpdomain
|
||||
|
||||
@@ -19,4 +19,3 @@ 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:
|
||||
|
||||
@@ -31,41 +31,41 @@ dependencies = [
|
||||
"BeautifulSoup4==4.12.*",
|
||||
"bleach==5.0.*",
|
||||
"celery==5.3.*",
|
||||
"chardet==5.1.*",
|
||||
"chardet==5.2.*",
|
||||
"cryptography>=3.4.2",
|
||||
"css-inline==0.8.*",
|
||||
"css-inline==0.13.*",
|
||||
"defusedcsv>=1.1.0",
|
||||
"dj-static",
|
||||
"Django==4.2.*",
|
||||
"django-bootstrap3==23.1.*",
|
||||
"django-compressor==4.3.*",
|
||||
"django-bootstrap3==23.6.*",
|
||||
"django-compressor==4.4",
|
||||
"django-countries==7.5.*",
|
||||
"django-filter==23.2",
|
||||
"django-filter==23.5",
|
||||
"django-formset-js-improved==0.5.0.3",
|
||||
"django-formtools==2.5.1",
|
||||
"django-hierarkey==1.1.*",
|
||||
"django-hijack==3.3.*",
|
||||
"django-hijack==3.4.*",
|
||||
"django-i18nfield==1.9.*,>=1.9.4",
|
||||
"django-libsass==0.9",
|
||||
"django-localflavor==4.0",
|
||||
"django-markup",
|
||||
"django-oauth-toolkit==2.2.*",
|
||||
"django-otp==1.2.*",
|
||||
"django-phonenumber-field==7.1.*",
|
||||
"django-oauth-toolkit==2.3.*",
|
||||
"django-otp==1.3.*",
|
||||
"django-phonenumber-field==7.3.*",
|
||||
"django-redis==5.4.*",
|
||||
"django-scopes==2.0.*",
|
||||
"django-statici18n==2.4.*",
|
||||
"djangorestframework==3.14.*",
|
||||
"dnspython==2.3.*",
|
||||
"dnspython==2.5.*",
|
||||
"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.22.*",
|
||||
"libsass==0.23.*",
|
||||
"lxml",
|
||||
"markdown==3.4.3", # 3.3.5 requires importlib-metadata>=4.4, but django-bootstrap3 requires importlib-metadata<3.
|
||||
"markdown==3.5.2", # 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,15 +73,15 @@ dependencies = [
|
||||
"packaging",
|
||||
"paypalrestsdk==1.13.*",
|
||||
"paypal-checkout-serversdk==1.0.*",
|
||||
"PyJWT==2.7.*",
|
||||
"PyJWT==2.8.*",
|
||||
"phonenumberslite==8.13.*",
|
||||
"Pillow==9.5.*",
|
||||
"Pillow==10.2.*",
|
||||
"pretix-plugin-build",
|
||||
"protobuf==4.23.*",
|
||||
"protobuf==4.25.*",
|
||||
"psycopg2-binary",
|
||||
"pycountry",
|
||||
"pycparser==2.21",
|
||||
"pycryptodome==3.18.*",
|
||||
"pycryptodome==3.20.*",
|
||||
"pypdf==3.9.*",
|
||||
"python-bidi==0.4.*", # Support for Arabic in reportlab
|
||||
"python-dateutil==2.8.*",
|
||||
@@ -89,8 +89,8 @@ dependencies = [
|
||||
"pytz-deprecation-shim==0.1.*",
|
||||
"pyuca",
|
||||
"qrcode==7.4.*",
|
||||
"redis==4.6.*",
|
||||
"reportlab==4.0.*",
|
||||
"redis==5.0.*",
|
||||
"reportlab==4.1.*",
|
||||
"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==0.4.*",
|
||||
"webauthn==2.0.*",
|
||||
"zeep==4.2.*"
|
||||
]
|
||||
|
||||
@@ -112,23 +112,21 @@ dev = [
|
||||
"aiohttp==3.9.*",
|
||||
"coverage",
|
||||
"coveralls",
|
||||
"fakeredis==2.18.*",
|
||||
"flake8==6.0.*",
|
||||
"fakeredis==2.21.*",
|
||||
"flake8==7.0.*",
|
||||
"freezegun",
|
||||
"isort==5.12.*",
|
||||
"isort==5.13.*",
|
||||
"pep8-naming==0.13.*",
|
||||
"potypo",
|
||||
"pycodestyle==2.10.*",
|
||||
"pyflakes==3.0.*",
|
||||
"pytest-asyncio",
|
||||
"pytest-cache",
|
||||
"pytest-cov",
|
||||
"pytest-django==4.*",
|
||||
"pytest-mock==3.10.*",
|
||||
"pytest-mock==3.12.*",
|
||||
"pytest-rerunfailures==13.*",
|
||||
"pytest-sugar",
|
||||
"pytest-xdist==3.3.*",
|
||||
"pytest==7.3.*",
|
||||
"pytest-xdist==3.5.*",
|
||||
"pytest==8.0.*",
|
||||
"responses",
|
||||
]
|
||||
|
||||
|
||||
@@ -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.2.0.dev0"
|
||||
__version__ = "2024.3.0.dev0"
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# 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=""),
|
||||
),
|
||||
]
|
||||
@@ -42,6 +42,11 @@ 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
|
||||
|
||||
@@ -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')
|
||||
'valid_from', 'valid_until', 'blocked', 'voucher_budget_use')
|
||||
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'
|
||||
'seat', 'canceled', 'discount', 'valid_from', 'valid_until', 'blocked', 'voucher_budget_use'
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
@@ -35,6 +35,7 @@ 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
|
||||
@@ -586,6 +587,32 @@ 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())
|
||||
|
||||
@@ -189,7 +189,7 @@ class TemplateBasedMailRenderer(BaseHTMLMailRenderer):
|
||||
tpl = get_template(self.template_name)
|
||||
body_html = tpl.render(htmlctx)
|
||||
|
||||
inliner = css_inline.CSSInliner(remove_style_tags=True)
|
||||
inliner = css_inline.CSSInliner(keep_style_tags=False)
|
||||
body_html = inliner.inline(body_html)
|
||||
|
||||
return body_html
|
||||
|
||||
@@ -86,6 +86,7 @@ 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 '
|
||||
|
||||
@@ -257,8 +257,8 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
tax_rates = self._get_all_tax_rates(qs)
|
||||
|
||||
headers = [
|
||||
_('Event slug'), _('Order code'), _('Order total'), _('Status'), _('Email'), _('Phone number'),
|
||||
_('Order date'), _('Order time'), _('Company'), _('Name'),
|
||||
_('Event slug'), _('Event name'), _('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,6 +335,7 @@ 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(),
|
||||
@@ -436,6 +437,7 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
|
||||
headers = [
|
||||
_('Event slug'),
|
||||
_('Event name'),
|
||||
_('Order code'),
|
||||
_('Status'),
|
||||
_('Email'),
|
||||
@@ -472,6 +474,7 @@ 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,
|
||||
@@ -550,6 +553,7 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
|
||||
headers = [
|
||||
_('Event slug'),
|
||||
_('Event name'),
|
||||
_('Order code'),
|
||||
_('Position ID'),
|
||||
_('Status'),
|
||||
@@ -651,6 +655,7 @@ 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(),
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
#
|
||||
# This file is part of pretix (Community Edition).
|
||||
#
|
||||
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||
# Copyright (C) 2020-2021 rami.io GmbH and contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
|
||||
# Public License as published by the Free Software Foundation in version 3 of the License.
|
||||
#
|
||||
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
|
||||
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
|
||||
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
|
||||
# this file, see <https://pretix.eu/about/en/license>.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
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
|
||||
@@ -182,7 +182,7 @@ class BaseReportlabInvoiceRenderer(BaseInvoiceRenderer):
|
||||
pdfmetrics.registerFontFamily('OpenSans', normal='OpenSans', bold='OpenSansBd',
|
||||
italic='OpenSansIt', boldItalic='OpenSansBI')
|
||||
|
||||
for family, styles in get_fonts(event=self.event, pdf_support_required=True).items():
|
||||
for family, styles in get_fonts().items():
|
||||
if family == self.event.settings.invoice_renderer_font:
|
||||
pdfmetrics.registerFont(TTFont(family, finders.find(styles['regular']['truetype'])))
|
||||
self.font_regular = family
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
from collections import OrderedDict
|
||||
from urllib.parse import urlparse, urlsplit
|
||||
from urllib.parse import urlsplit
|
||||
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
|
||||
|
||||
from django.conf import settings
|
||||
@@ -40,7 +40,6 @@ 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
|
||||
|
||||
@@ -241,14 +240,6 @@ 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:
|
||||
@@ -268,14 +259,6 @@ 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}'],
|
||||
@@ -284,7 +267,7 @@ class SecurityMiddleware(MiddlewareMixin):
|
||||
'style-src': ["{static}", "{media}"],
|
||||
'connect-src': ["{dynamic}", "{media}"],
|
||||
'img-src': ["{static}", "{media}", "data:"] + img_src,
|
||||
'font-src': ["{static}"] + list(font_src),
|
||||
'font-src': ["{static}"],
|
||||
'media-src': ["{static}", "data:"],
|
||||
# form-action is not only used to match on form actions, but also on URLs
|
||||
# form-actions redirect to. In the context of e.g. payment providers or
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# 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,
|
||||
),
|
||||
]
|
||||
@@ -37,9 +37,7 @@ 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,
|
||||
@@ -53,11 +51,12 @@ 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, websafe_encode
|
||||
from ...helpers.u2f import pub_key_from_der, websafe_decode
|
||||
from .base import LoggingMixin
|
||||
|
||||
|
||||
@@ -606,7 +605,12 @@ class U2FDevice(Device):
|
||||
json_data = models.TextField()
|
||||
|
||||
@property
|
||||
def webauthnuser(self):
|
||||
def webauthndevice(self):
|
||||
d = json.loads(self.json_data)
|
||||
return PublicKeyCredentialDescriptor(websafe_decode(d['keyHandle']))
|
||||
|
||||
@property
|
||||
def webauthnpubkey(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
|
||||
@@ -618,16 +622,7 @@ class U2FDevice(Device):
|
||||
pub_key.public_numbers().x, pub_key.public_numbers().y
|
||||
)
|
||||
)
|
||||
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
|
||||
)
|
||||
return pub_key
|
||||
|
||||
|
||||
class WebAuthnDevice(Device):
|
||||
@@ -639,14 +634,9 @@ class WebAuthnDevice(Device):
|
||||
sign_count = models.IntegerField(default=0)
|
||||
|
||||
@property
|
||||
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
|
||||
)
|
||||
def webauthndevice(self):
|
||||
return PublicKeyCredentialDescriptor(websafe_decode(self.credential_id))
|
||||
|
||||
@property
|
||||
def webauthnpubkey(self):
|
||||
return websafe_decode(self.pub_key)
|
||||
|
||||
@@ -80,6 +80,15 @@ from .organizer import Organizer, Team
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def annotate_with_time_based_properties(events_or_subevents, now_dt):
|
||||
print("annotate_with_time_based_properties", now_dt)
|
||||
for e_s in events_or_subevents:
|
||||
if e_s:
|
||||
e_s.presale_is_running = e_s.presale_is_running_by_time(now_dt)
|
||||
e_s.presale_has_ended = e_s.presale_has_ended_by_time(now_dt)
|
||||
return events_or_subevents
|
||||
|
||||
|
||||
class EventMixin:
|
||||
def clean(self):
|
||||
if self.presale_start and self.presale_end and self.presale_start > self.presale_end:
|
||||
@@ -229,17 +238,17 @@ class EventMixin:
|
||||
else:
|
||||
return self.presale_end
|
||||
|
||||
@property
|
||||
def presale_has_ended(self):
|
||||
def presale_has_ended_by_time(self, now_dt: datetime=None):
|
||||
"""
|
||||
Is true, when ``presale_end`` is set and in the past.
|
||||
"""
|
||||
now_dt = now_dt or now()
|
||||
if self.effective_presale_end:
|
||||
return now() > self.effective_presale_end
|
||||
return now_dt > self.effective_presale_end
|
||||
elif self.date_to:
|
||||
return now() > self.date_to
|
||||
return now_dt > self.date_to
|
||||
else:
|
||||
return now().astimezone(self.timezone).date() > self.date_from.astimezone(self.timezone).date()
|
||||
return now_dt.astimezone(self.timezone).date() > self.date_from.astimezone(self.timezone).date()
|
||||
|
||||
@property
|
||||
def effective_presale_start(self):
|
||||
@@ -253,18 +262,21 @@ class EventMixin:
|
||||
else:
|
||||
return self.presale_start
|
||||
|
||||
@property
|
||||
def presale_is_running(self):
|
||||
def presale_is_running_by_time(self, now_dt: datetime=None):
|
||||
"""
|
||||
Is true, when ``presale_end`` is not set or in the future and ``presale_start`` is not
|
||||
set or in the past.
|
||||
"""
|
||||
if self.effective_presale_start and now() < self.effective_presale_start:
|
||||
now_dt = now_dt or now()
|
||||
if self.effective_presale_start and now_dt < self.effective_presale_start:
|
||||
return False
|
||||
return not self.presale_has_ended
|
||||
return not self.presale_has_ended_by_time(now_dt)
|
||||
|
||||
@property
|
||||
def event_microdata(self):
|
||||
if self.settings.event_microdata:
|
||||
return self.settings.event_microdata
|
||||
|
||||
import json
|
||||
|
||||
eventdict = {
|
||||
@@ -680,12 +692,12 @@ class Event(EventMixin, LoggedModel):
|
||||
|
||||
return qs_annotated
|
||||
|
||||
@property
|
||||
def presale_has_ended(self):
|
||||
def presale_has_ended_by_time(self, now_dt: datetime = None):
|
||||
now_dt = now_dt or now()
|
||||
if self.has_subevents:
|
||||
return self.presale_end and now() > self.presale_end
|
||||
return self.presale_end and now_dt > self.presale_end
|
||||
else:
|
||||
return super().presale_has_ended
|
||||
return super().presale_has_ended_by_time(now_dt)
|
||||
|
||||
def delete_all_orders(self, really=False):
|
||||
from .checkin import Checkin
|
||||
|
||||
@@ -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, null=True
|
||||
max_digits=13, decimal_places=2,
|
||||
)
|
||||
free_price = models.BooleanField(
|
||||
default=False,
|
||||
|
||||
@@ -351,9 +351,6 @@ 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:
|
||||
@@ -431,7 +428,15 @@ class Voucher(LoggedModel):
|
||||
elif old_instance.variation:
|
||||
quotas |= set(old_instance.variation.quotas.filter(subevent=old_instance.subevent))
|
||||
elif old_instance.item:
|
||||
quotas |= set(old_instance.item.quotas.filter(subevent=old_instance.subevent))
|
||||
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))
|
||||
return quotas
|
||||
|
||||
@staticmethod
|
||||
@@ -446,13 +451,19 @@ 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.'))
|
||||
|
||||
@@ -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 Event, Order, OrderPosition, Question
|
||||
from pretix.base.models import 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,10 +738,9 @@ 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, event: Event = None):
|
||||
def _register_fonts(cls):
|
||||
if hasattr(cls, '_fonts_registered'):
|
||||
return
|
||||
pdfmetrics.registerFont(TTFont('Open Sans', finders.find('fonts/OpenSans-Regular.ttf')))
|
||||
@@ -749,7 +748,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(event, pdf_support_required=True).items():
|
||||
for family, styles in get_fonts().items():
|
||||
pdfmetrics.registerFont(TTFont(family, finders.find(styles['regular']['truetype'])))
|
||||
if 'italic' in styles:
|
||||
pdfmetrics.registerFont(TTFont(family + ' I', finders.find(styles['italic']['truetype'])))
|
||||
@@ -940,12 +939,6 @@ 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
|
||||
|
||||
@@ -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 />')
|
||||
invoice.additional_text = str(additional).replace('\n', '<br />')
|
||||
invoice.introductory_text = str(introductory).replace('\n', '<br />').replace('\r', '')
|
||||
invoice.additional_text = str(additional).replace('\n', '<br />').replace('\r', '')
|
||||
invoice.footer_text = str(footer)
|
||||
invoice.payment_provider_text = str(payment).replace('\n', '<br />')
|
||||
invoice.payment_provider_text = str(payment).replace('\n', '<br />').replace('\r', '')
|
||||
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 />')
|
||||
invoice.additional_text = str(additional).replace('\n', '<br />')
|
||||
invoice.introductory_text = str(introductory).replace('\n', '<br />').replace('\r', '')
|
||||
invoice.additional_text = str(additional).replace('\n', '<br />').replace('\r', '')
|
||||
invoice.footer_text = str(footer)
|
||||
invoice.payment_provider_text = str(payment).replace('\n', '<br />')
|
||||
invoice.payment_provider_text = str(payment).replace('\n', '<br />').replace('\r', '')
|
||||
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_rate=tax.rate, tax_name=tax.name
|
||||
)
|
||||
else:
|
||||
for i in range(5):
|
||||
|
||||
@@ -383,6 +383,7 @@ 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:
|
||||
|
||||
@@ -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(remove_style_tags=True)
|
||||
inliner = css_inline.CSSInliner(keep_style_tags=False)
|
||||
body_html = inliner.inline(body_html)
|
||||
|
||||
tpl_plain = get_template('pretixbase/email/notification.txt')
|
||||
|
||||
@@ -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(list)
|
||||
self._var_to_quotas = defaultdict(list)
|
||||
self._item_to_quotas = defaultdict(set)
|
||||
self._var_to_quotas = defaultdict(set)
|
||||
self._early_out = early_out
|
||||
self._quota_objects = {}
|
||||
self.results = {}
|
||||
@@ -243,13 +243,16 @@ 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']].append(self._quota_objects[m['quota_id']])
|
||||
self._item_to_quotas[m['item_id']].add(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')
|
||||
).values('quota_id', 'itemvariation_id', 'itemvariation__item_id')
|
||||
for m in q_vars:
|
||||
self._var_to_quotas[m['itemvariation_id']].append(self._quota_objects[m['quota_id']])
|
||||
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._compute_orders(quotas, q_items, q_vars, size_left)
|
||||
|
||||
@@ -378,7 +381,10 @@ 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})
|
||||
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(
|
||||
variation_id__in={i['itemvariation_id'] for i in q_vars if i['quota_id'] in quota_ids}
|
||||
) | Q(
|
||||
|
||||
@@ -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(pdf_support_required=False).items()
|
||||
(a, {"title": a, "data": v}) for a, v in get_fonts().items() if not v.get('pdf_only', False)
|
||||
], key=lambda a: a[0])
|
||||
return {
|
||||
'choices': choices,
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
{# 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 %}
|
||||
@@ -123,8 +123,6 @@ class ClearableBasenameFileInput(forms.ClearableFileInput):
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
if hasattr(self.file, 'display_name'):
|
||||
return self.file.display_name
|
||||
return self.file.name
|
||||
|
||||
@property
|
||||
@@ -219,15 +217,17 @@ class ExtValidationMixin:
|
||||
|
||||
def clean(self, *args, **kwargs):
|
||||
data = super().clean(*args, **kwargs)
|
||||
if isinstance(data, UploadedFile):
|
||||
filename = data.name
|
||||
|
||||
from ...base.models import CachedFile
|
||||
if isinstance(data, (UploadedFile, CachedFile)):
|
||||
filename = data.name if isinstance(data, UploadedFile) else data.filename
|
||||
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)
|
||||
validate_uploaded_file_for_valid_image(data if isinstance(data, UploadedFile) else data.file)
|
||||
|
||||
return data
|
||||
|
||||
@@ -257,6 +257,12 @@ 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(),
|
||||
@@ -268,6 +274,9 @@ 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):
|
||||
|
||||
@@ -79,7 +79,6 @@ 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):
|
||||
@@ -652,9 +651,6 @@ 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 = []
|
||||
@@ -935,9 +931,6 @@ 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):
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
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
|
||||
@@ -27,8 +28,6 @@ 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):
|
||||
"""
|
||||
|
||||
@@ -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" organizer=request.organizer.slug event=request.event.slug %}">
|
||||
<link type="text/css" rel="stylesheet" href="{% url "control:pdf.css" %}">
|
||||
{% endblock %}
|
||||
{% block inside %}
|
||||
<h1>{% trans "General settings" %}</h1>
|
||||
|
||||
@@ -70,6 +70,10 @@
|
||||
{% 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">
|
||||
|
||||
@@ -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" organizer=request.organizer.slug event=request.event.slug %}">
|
||||
<link type="text/css" rel="stylesheet" href="{% url "control:pdf.css" %}">
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<h1>
|
||||
@@ -234,7 +234,62 @@
|
||||
</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"
|
||||
@@ -277,6 +332,30 @@
|
||||
</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>
|
||||
@@ -335,83 +414,6 @@
|
||||
</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">
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{% load static %}
|
||||
|
||||
@font-face {
|
||||
font-family: 'AND';
|
||||
font-style: normal;
|
||||
@@ -15,7 +14,7 @@
|
||||
|
||||
{% for family, styles in fonts.items %}
|
||||
{% for style, formats in styles.items %}
|
||||
{% if "sample" not in style and "pdf_only" not in style %}
|
||||
{% if "sample" not in style %}
|
||||
@font-face {
|
||||
font-family: '{{ family }}';
|
||||
{% if style == "italic" or style == "bolditalic" %}
|
||||
@@ -28,9 +27,9 @@
|
||||
{% else %}
|
||||
font-weight: normal;
|
||||
{% 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 %};
|
||||
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 %};
|
||||
}
|
||||
.preload-font[data-family="{{family}}"][data-style="{{style}}"] {
|
||||
font-family: '{{ family }}', 'AND';
|
||||
|
||||
@@ -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
|
||||
from urllib.parse import quote, urlparse
|
||||
|
||||
import webauthn
|
||||
from django.conf import settings
|
||||
@@ -54,6 +54,7 @@ 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 (
|
||||
@@ -62,7 +63,6 @@ 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,6 +389,10 @@ 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'
|
||||
|
||||
@@ -427,25 +431,41 @@ 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:
|
||||
wu = d.webauthnuser
|
||||
|
||||
if isinstance(d, U2FDevice):
|
||||
# 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
|
||||
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.verify()
|
||||
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:
|
||||
logger.exception('U2F login failed')
|
||||
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')
|
||||
else:
|
||||
if isinstance(d, WebAuthnDevice):
|
||||
d.sign_count = sign_count
|
||||
@@ -471,23 +491,24 @@ class Login2FAView(TemplateView):
|
||||
ctx = super().get_context_data()
|
||||
if 'webauthn_challenge' in self.request.session:
|
||||
del self.request.session['webauthn_challenge']
|
||||
challenge = generate_challenge(32)
|
||||
self.request.session['webauthn_challenge'] = challenge
|
||||
challenge = generate_challenge()
|
||||
self.request.session['webauthn_challenge'] = base64.b64encode(challenge).decode()
|
||||
devices = [
|
||||
device.webauthnuser for device in WebAuthnDevice.objects.filter(confirmed=True, user=self.user)
|
||||
device.webauthndevice for device in WebAuthnDevice.objects.filter(confirmed=True, user=self.user)
|
||||
] + [
|
||||
device.webauthnuser for device in U2FDevice.objects.filter(confirmed=True, user=self.user)
|
||||
device.webauthndevice for device in U2FDevice.objects.filter(confirmed=True, user=self.user)
|
||||
]
|
||||
if devices:
|
||||
webauthn_assertion_options = webauthn.WebAuthnAssertionOptions(
|
||||
devices,
|
||||
challenge
|
||||
auth_options = webauthn.generate_authentication_options(
|
||||
rp_id=get_webauthn_rp_id(self.request),
|
||||
challenge=challenge,
|
||||
allow_credentials=devices,
|
||||
)
|
||||
ad = webauthn_assertion_options.assertion_dict
|
||||
ad['extensions'] = {
|
||||
'appid': get_u2f_appid(self.request)
|
||||
}
|
||||
ctx['jsondata'] = json.dumps(ad)
|
||||
|
||||
# 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)
|
||||
return ctx
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
|
||||
@@ -262,7 +262,7 @@ class BaseEditorView(EventPermissionRequiredMixin, TemplateView):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['fonts'] = get_fonts(self.request.event, pdf_support_required=True)
|
||||
ctx['fonts'] = get_fonts()
|
||||
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(self.request.event if hasattr(self.request, 'event') else None, pdf_support_required=True)
|
||||
ctx['fonts'] = get_fonts()
|
||||
return ctx
|
||||
|
||||
|
||||
|
||||
@@ -674,6 +674,7 @@ def itemvar_select2(request, **kwargs):
|
||||
{
|
||||
'id': k,
|
||||
'text': str(v),
|
||||
'inactive': d,
|
||||
}
|
||||
for k, v, d in choices
|
||||
],
|
||||
@@ -715,6 +716,7 @@ def itemvars_select2(request, **kwargs):
|
||||
{
|
||||
'id': k,
|
||||
'text': str(v),
|
||||
'inactive': d,
|
||||
}
|
||||
for k, v, d in choices
|
||||
],
|
||||
|
||||
@@ -35,10 +35,9 @@
|
||||
import base64
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
from collections import defaultdict
|
||||
from urllib.parse import quote, urlparse
|
||||
from urllib.parse import quote
|
||||
|
||||
import webauthn
|
||||
from django.conf import settings
|
||||
@@ -57,6 +56,7 @@ 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
|
||||
from pretix.control.views.auth import get_u2f_appid, get_webauthn_rp_id
|
||||
from pretix.helpers.http import redirect_to_url
|
||||
from pretix.helpers.webauthn import generate_challenge, generate_ukey
|
||||
from pretix.helpers.u2f import websafe_encode
|
||||
|
||||
REAL_DEVICE_TYPES = (TOTPDevice, WebAuthnDevice, U2FDevice)
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -105,25 +105,41 @@ 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:
|
||||
wu = d.webauthnuser
|
||||
|
||||
if isinstance(d, U2FDevice):
|
||||
# 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
|
||||
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.verify()
|
||||
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:
|
||||
logger.exception('U2F login failed')
|
||||
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')
|
||||
else:
|
||||
if isinstance(d, WebAuthnDevice):
|
||||
d.sign_count = sign_count
|
||||
@@ -162,23 +178,24 @@ class ReauthView(TemplateView):
|
||||
ctx = super().get_context_data()
|
||||
if 'webauthn_challenge' in self.request.session:
|
||||
del self.request.session['webauthn_challenge']
|
||||
challenge = generate_challenge(32)
|
||||
self.request.session['webauthn_challenge'] = challenge
|
||||
challenge = generate_challenge()
|
||||
self.request.session['webauthn_challenge'] = base64.b64encode(challenge).decode()
|
||||
devices = [
|
||||
device.webauthnuser for device in WebAuthnDevice.objects.filter(confirmed=True, user=self.request.user)
|
||||
device.webauthndevice for device in WebAuthnDevice.objects.filter(confirmed=True, user=self.request.user)
|
||||
] + [
|
||||
device.webauthnuser for device in U2FDevice.objects.filter(confirmed=True, user=self.request.user)
|
||||
device.webauthndevice for device in U2FDevice.objects.filter(confirmed=True, user=self.request.user)
|
||||
]
|
||||
if devices:
|
||||
webauthn_assertion_options = webauthn.WebAuthnAssertionOptions(
|
||||
devices,
|
||||
challenge
|
||||
auth_options = webauthn.generate_authentication_options(
|
||||
rp_id=get_webauthn_rp_id(self.request),
|
||||
challenge=challenge,
|
||||
allow_credentials=devices,
|
||||
)
|
||||
ad = webauthn_assertion_options.assertion_dict
|
||||
ad['extensions'] = {
|
||||
'appid': get_u2f_appid(self.request)
|
||||
}
|
||||
ctx['jsondata'] = json.dumps(ad)
|
||||
|
||||
# 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)
|
||||
ctx['form'] = self.form
|
||||
return ctx
|
||||
|
||||
@@ -387,23 +404,26 @@ class User2FADeviceConfirmWebAuthnView(RecentAuthenticationRequiredMixin, Templa
|
||||
if 'webauthn_challenge' in self.request.session:
|
||||
del self.request.session['webauthn_challenge']
|
||||
|
||||
challenge = generate_challenge(32)
|
||||
ukey = generate_ukey()
|
||||
challenge = generate_challenge()
|
||||
ukey = generate_user_handle()
|
||||
|
||||
self.request.session['webauthn_challenge'] = challenge
|
||||
self.request.session['webauthn_register_ukey'] = ukey
|
||||
self.request.session['webauthn_challenge'] = base64.b64encode(challenge).decode()
|
||||
self.request.session['webauthn_register_ukey'] = base64.b64encode(ukey).decode()
|
||||
|
||||
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"
|
||||
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,
|
||||
)
|
||||
ctx['jsondata'] = json.dumps(make_credential_options.registration_dict)
|
||||
ctx['jsondata'] = webauthn.options_to_json(make_credential_options)
|
||||
|
||||
return ctx
|
||||
|
||||
@@ -412,30 +432,13 @@ 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
|
||||
|
||||
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
|
||||
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_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
|
||||
@@ -443,7 +446,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=webauthn_credential.credential_id
|
||||
credential_id=registration_verification.credential_id
|
||||
).first()
|
||||
if credential_id_exists:
|
||||
messages.error(request, _('This security device is already registered.'))
|
||||
@@ -451,14 +454,11 @@ class User2FADeviceConfirmWebAuthnView(RecentAuthenticationRequiredMixin, Templa
|
||||
'device': self.device.pk
|
||||
}))
|
||||
|
||||
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.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)
|
||||
self.device.icon_url = settings.SITE_URL
|
||||
self.device.confirmed = True
|
||||
self.device.save()
|
||||
|
||||
@@ -32,9 +32,14 @@ def clean_filename(fname):
|
||||
|
||||
"Terms.pdf" → "Terms.pdf.OybgvyAH.22c0583727d5bc.pdf"
|
||||
|
||||
This function reverses this operation:
|
||||
This function reverses this operation (leaving names without doubled extension as-is):
|
||||
|
||||
"Terms.pdf.OybgvyAH.22c0583727d5bc.pdf" → "Terms.pdf"
|
||||
"Terms.pdf" → "Terms.pdf"
|
||||
"""
|
||||
ext = '.' + fname.split('.')[-1]
|
||||
return fname.rsplit(ext + ".", 1)[0] + ext
|
||||
parts = fname.rsplit(ext + ".", 1)
|
||||
if len(parts) == 1:
|
||||
return parts[0]
|
||||
else:
|
||||
return parts[0] + ext
|
||||
|
||||
@@ -44,11 +44,12 @@ 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:
|
||||
if hasattr(f, 'read'):
|
||||
file = BytesIO(f.read())
|
||||
else:
|
||||
file = BytesIO(f['content'])
|
||||
file = BytesIO(f['content'])
|
||||
|
||||
try:
|
||||
try:
|
||||
|
||||
@@ -62,7 +62,7 @@ def soft_equals(a, b):
|
||||
|
||||
def hard_equals(a, b):
|
||||
"""Implements the '===' operator."""
|
||||
if type(a) != type(b):
|
||||
if type(a) is not type(b):
|
||||
return False
|
||||
return a == b
|
||||
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
#
|
||||
# This file is part of pretix (Community Edition).
|
||||
#
|
||||
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||
# Copyright (C) 2020-2021 rami.io GmbH and contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
|
||||
# Public License as published by the Free Software Foundation in version 3 of the License.
|
||||
#
|
||||
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
|
||||
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
|
||||
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
|
||||
# this file, see <https://pretix.eu/about/en/license>.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
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
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
|
||||
"POT-Creation-Date: 2024-02-28 13:11+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:784
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:791
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:787
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:794
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:945
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:952
|
||||
msgid "Use a different name internally"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:985
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:992
|
||||
msgid "Click to close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1060
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1067
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
|
||||
"POT-Creation-Date: 2024-02-28 13:11+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:784
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:791
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:787
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:794
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:945
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:952
|
||||
msgid "Use a different name internally"
|
||||
msgstr "قم باستخدم اسم مختلف داخليا"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:985
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:992
|
||||
msgid "Click to close"
|
||||
msgstr "اضغط لاغلاق الصفحة"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1060
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1067
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "لم تقم بحفظ التعديلات!"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
|
||||
"POT-Creation-Date: 2024-02-28 13:11+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:784
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:791
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:787
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:794
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:945
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:952
|
||||
msgid "Use a different name internally"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:985
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:992
|
||||
msgid "Click to close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1060
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1067
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
|
||||
"POT-Creation-Date: 2024-02-28 13:11+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:784
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:791
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:787
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:794
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:945
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:952
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Interně používat jiný název"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:985
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:992
|
||||
msgid "Click to close"
|
||||
msgstr "Kliknutím zavřete"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1060
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1067
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Máte neuložené změny!"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
|
||||
"POT-Creation-Date: 2024-02-28 13:11+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:784
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:791
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:787
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:794
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:945
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:952
|
||||
msgid "Use a different name internally"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:985
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:992
|
||||
msgid "Click to close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1060
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1067
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
|
||||
"POT-Creation-Date: 2024-02-28 13:11+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:784
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:791
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:787
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:794
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:945
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:952
|
||||
msgid "Use a different name internally"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:985
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:992
|
||||
msgid "Click to close"
|
||||
msgstr "Klik for at lukke"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1060
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1067
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Du har ændringer, der ikke er gemt!"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
|
||||
"POT-Creation-Date: 2024-02-28 13:11+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:784
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:791
|
||||
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:787
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:794
|
||||
msgid "Invalid page number."
|
||||
msgstr "Ungültige Seitenzahl."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:945
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:952
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Intern einen anderen Namen verwenden"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:985
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:992
|
||||
msgid "Click to close"
|
||||
msgstr "Klicken zum Schließen"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1060
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1067
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Sie haben ungespeicherte Änderungen!"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
|
||||
"POT-Creation-Date: 2024-02-28 13:11+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:784
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:791
|
||||
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:787
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:794
|
||||
msgid "Invalid page number."
|
||||
msgstr "Ungültige Seitenzahl."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:945
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:952
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Intern einen anderen Namen verwenden"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:985
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:992
|
||||
msgid "Click to close"
|
||||
msgstr "Klicken zum Schließen"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1060
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1067
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Du hast ungespeicherte Änderungen!"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
|
||||
"POT-Creation-Date: 2024-02-28 13:11+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:784
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:791
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:787
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:794
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:945
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:952
|
||||
msgid "Use a different name internally"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:985
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:992
|
||||
msgid "Click to close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1060
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1067
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
|
||||
"POT-Creation-Date: 2024-02-28 13:11+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:784
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:791
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:787
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:794
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:945
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:952
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Χρησιμοποιήστε διαφορετικό όνομα εσωτερικά"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:985
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:992
|
||||
msgid "Click to close"
|
||||
msgstr "Κάντε κλικ για να κλείσετε"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1060
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1067
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
|
||||
"POT-Creation-Date: 2024-02-28 13:11+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:784
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:791
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:787
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:794
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:945
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:952
|
||||
msgid "Use a different name internally"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:985
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:992
|
||||
msgid "Click to close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1060
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1067
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
|
||||
"POT-Creation-Date: 2024-02-28 13:11+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:784
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:791
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:787
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:794
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:945
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:952
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Usar un nombre diferente internamente"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:985
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:992
|
||||
msgid "Click to close"
|
||||
msgstr "Click para cerrar"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1060
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1067
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "¡Tienes cambios sin guardar!"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
|
||||
"POT-Creation-Date: 2024-02-28 13:11+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:784
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:791
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:787
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:794
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:945
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:952
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Käytä toista nimeä sisäisesti"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:985
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:992
|
||||
msgid "Click to close"
|
||||
msgstr "Sulje klikkaamalla"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1060
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1067
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Sinulla on tallentamattomia muutoksia!"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: French\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
|
||||
"POT-Creation-Date: 2024-02-28 13:11+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:784
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:791
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:787
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:794
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:945
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:952
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Utiliser un nom différent en interne"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:985
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:992
|
||||
msgid "Click to close"
|
||||
msgstr "Cliquez pour fermer"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1060
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1067
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Vous avez des modifications non sauvegardées !"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
|
||||
"POT-Creation-Date: 2024-02-28 13:11+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:784
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:791
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:787
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:794
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:945
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:952
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Usar un nome diferente internamente"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:985
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:992
|
||||
msgid "Click to close"
|
||||
msgstr "Click para cerrar"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1060
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1067
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Tes cambios sen gardar!"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
|
||||
"POT-Creation-Date: 2024-02-28 13:11+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:784
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:791
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:787
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:794
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:945
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:952
|
||||
msgid "Use a different name internally"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:985
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:992
|
||||
msgid "Click to close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1060
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1067
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
|
||||
"POT-Creation-Date: 2024-02-28 13:11+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:784
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:791
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:787
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:794
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:945
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:952
|
||||
msgid "Use a different name internally"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:985
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:992
|
||||
msgid "Click to close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1060
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1067
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
|
||||
"POT-Creation-Date: 2024-02-28 13:11+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:784
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:791
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:787
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:794
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:945
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:952
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Használj másik nevet"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:985
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:992
|
||||
msgid "Click to close"
|
||||
msgstr "Bezárásért kattints"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1060
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1067
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Mentetlen változtatások!"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
|
||||
"POT-Creation-Date: 2024-02-28 13:11+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:784
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:791
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:787
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:794
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:945
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:952
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Gunakan nama yang berbeda secara internal"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:985
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:992
|
||||
msgid "Click to close"
|
||||
msgstr "Klik untuk menutup"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1060
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1067
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Anda memiliki perubahan yang belum disimpan!"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
|
||||
"POT-Creation-Date: 2024-02-28 13:11+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:784
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:791
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:787
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:794
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:945
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:952
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Utilizza un nome diverso internamente"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:985
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:992
|
||||
msgid "Click to close"
|
||||
msgstr "Clicca per chiudere"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1060
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1067
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Hai cambiamenti non salvati!"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
|
||||
"POT-Creation-Date: 2024-02-28 13:11+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:784
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:791
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:787
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:794
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:945
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:952
|
||||
msgid "Use a different name internally"
|
||||
msgstr "内部で別の名前を使用してください"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:985
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:992
|
||||
msgid "Click to close"
|
||||
msgstr "クリックして閉じる"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1060
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1067
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "保存されていない変更があります!"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
|
||||
"POT-Creation-Date: 2024-02-28 13:11+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:784
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:791
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:787
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:794
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:945
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:952
|
||||
msgid "Use a different name internally"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:985
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:992
|
||||
msgid "Click to close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1060
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1067
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
|
||||
"POT-Creation-Date: 2024-02-28 13:11+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:784
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:791
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:787
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:794
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:945
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:952
|
||||
msgid "Use a different name internally"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:985
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:992
|
||||
msgid "Click to close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1060
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1067
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
|
||||
"POT-Creation-Date: 2024-02-28 13:11+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:784
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:791
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:787
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:794
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:945
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:952
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Izmantojiet citu nosaukumu iekšēji"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:985
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:992
|
||||
msgid "Click to close"
|
||||
msgstr "Noklikšķiniet, lai aizvērtu"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1060
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1067
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Jums ir nesaglabātas izmaiņas!"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
|
||||
"POT-Creation-Date: 2024-02-28 13:11+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:784
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:791
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:787
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:794
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:945
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:952
|
||||
msgid "Use a different name internally"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:985
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:992
|
||||
msgid "Click to close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1060
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1067
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
|
||||
"POT-Creation-Date: 2024-02-28 13:11+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:784
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:791
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:787
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:794
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:945
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:952
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Bruk et annet navn internt"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:985
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:992
|
||||
msgid "Click to close"
|
||||
msgstr "Klikk for å lukke"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1060
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1067
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Du har ikke-lagrede endringer!"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-09 14:51+0000\n"
|
||||
"POT-Creation-Date: 2024-02-28 13:11+0000\n"
|
||||
"PO-Revision-Date: 2023-11-14 23:00+0000\n"
|
||||
"Last-Translator: Thomas Vranken <thvranken@gmail.com>\n"
|
||||
"Language-Team: Dutch <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
@@ -637,23 +637,23 @@ msgstr "Geen"
|
||||
msgid "Selected only"
|
||||
msgstr "Alleen geselecteerde"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:784
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:791
|
||||
msgid "Enter page number between 1 and %(max)s."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:787
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:794
|
||||
msgid "Invalid page number."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:945
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:952
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Gebruik intern een andere naam"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:985
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:992
|
||||
msgid "Click to close"
|
||||
msgstr "Klik om te sluiten"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1060
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1067
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "U heeft nog niet opgeslagen wijzigingen!"
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user