forked from CGM_Public/pretix_original
Compare commits
134
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e54837c532 | ||
|
|
bc49f0f7f1 | ||
|
|
3e122e0270 | ||
|
|
e8ea6e0f5c | ||
|
|
e94e5be878 | ||
|
|
1073ea626e | ||
|
|
23ab8df443 | ||
|
|
d6caf01a38 | ||
|
|
1424ae78e9 | ||
|
|
827382edc3 | ||
|
|
85482bc939 | ||
|
|
42ce545f2f | ||
|
|
e49bc5d78d | ||
|
|
6e7a32ef2a | ||
|
|
37df7a6313 | ||
|
|
d5951415a4 | ||
|
|
691159ed83 | ||
|
|
18f517af44 | ||
|
|
89ba2da7e7 | ||
|
|
c1c47e50c3 | ||
|
|
f262cd632c | ||
|
|
8d58294af1 | ||
|
|
ddc94a8a16 | ||
|
|
83811c0343 | ||
|
|
b2c05a72e5 | ||
|
|
8c56a23562 | ||
|
|
53e1d9c6c4 | ||
|
|
6250ab2165 | ||
|
|
6ada83df9a | ||
|
|
cfd6376936 | ||
|
|
edb0cd0941 | ||
|
|
88ac407cf3 | ||
|
|
5ba56fb5ac | ||
|
|
b51c9f7552 | ||
|
|
0853296663 | ||
|
|
721e7549bc | ||
|
|
aee86de330 | ||
|
|
756a4355d1 | ||
|
|
5119bbd0b1 | ||
|
|
728bd74e28 | ||
|
|
015ffeecbf | ||
|
|
0365f6d9fc | ||
|
|
e208a79c32 | ||
|
|
0037d37960 | ||
|
|
50d9b1e4a3 | ||
|
|
7919d012e6 | ||
|
|
327f95a9cc | ||
|
|
98946ded4b | ||
|
|
cf47b69bd3 | ||
|
|
fa5c69ce0a | ||
|
|
39d85fc112 | ||
|
|
23e222bf13 | ||
|
|
cb068b029f | ||
|
|
9e95f3be1b | ||
|
|
401c02865b | ||
|
|
062450002d | ||
|
|
6d834762c4 | ||
|
|
4f1e9a31c6 | ||
|
|
8ed3911dfb | ||
|
|
4562879cb2 | ||
|
|
ef0024b2ef | ||
|
|
8e603410fa | ||
|
|
16691ca2f6 | ||
|
|
d7e70fd0b9 | ||
|
|
071a3e2c9b | ||
|
|
1733c383b3 | ||
|
|
65ecdc184e | ||
|
|
63ae0724cf | ||
|
|
370d1bf06b | ||
|
|
06f361cece | ||
|
|
4b706339ed | ||
|
|
26213f2ba9 | ||
|
|
c183351d50 | ||
|
|
14131a7cec | ||
|
|
dfde308010 | ||
|
|
96b8631e09 | ||
|
|
84f464885d | ||
|
|
098147ce70 | ||
|
|
08b6186d77 | ||
|
|
e9e98a7821 | ||
|
|
3150c6a3ea | ||
|
|
898d1ab6ed | ||
|
|
52ae7626b0 | ||
|
|
c652911bfb | ||
|
|
52023cde09 | ||
|
|
b134f29cf6 | ||
|
|
19e1d132c2 | ||
|
|
393a218df5 | ||
|
|
f247eb0568 | ||
|
|
b35a388685 | ||
|
|
6dbbfe3b04 | ||
|
|
b2c49461bc | ||
|
|
23dcdf1fd1 | ||
|
|
1f80e9ef82 | ||
|
|
0969abb460 | ||
|
|
7b5789b110 | ||
|
|
f3b5996b82 | ||
|
|
5dcab59174 | ||
|
|
a2e38bb415 | ||
|
|
0510814aae | ||
|
|
dee2818f5d | ||
|
|
0d7809c36b | ||
|
|
4c494b5265 | ||
|
|
9e85e8c60a | ||
|
|
ab8c71fab8 | ||
|
|
1fa8ea3a12 | ||
|
|
f584d3d5af | ||
|
|
46ae911ade | ||
|
|
85db5698a6 | ||
|
|
09a17b57ce | ||
|
|
826962d6e2 | ||
|
|
f77e79bb38 | ||
|
|
d21e832204 | ||
|
|
119d4f0e04 | ||
|
|
feab6acfbd | ||
|
|
d85a6074ec | ||
|
|
6c813ea299 | ||
|
|
8a903f21ae | ||
|
|
a7f7c64cce | ||
|
|
82969daf37 | ||
|
|
8e9d0fb723 | ||
|
|
ef3d44e581 | ||
|
|
f9055fce9f | ||
|
|
cff0e86fd9 | ||
|
|
f0913fc720 | ||
|
|
23a9f60171 | ||
|
|
faf41c805c | ||
|
|
41cded095c | ||
|
|
90fb034897 | ||
|
|
f4203b7408 | ||
|
|
8a9f14db03 | ||
|
|
a2adf2825a | ||
|
|
8f7220b574 | ||
|
|
5adbdb80a8 |
@@ -35,7 +35,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: harmon758/postgresql-action@v1
|
- uses: harmon758/postgresql-action@v1
|
||||||
with:
|
with:
|
||||||
postgresql version: '11'
|
postgresql version: '15'
|
||||||
postgresql db: 'pretix'
|
postgresql db: 'pretix'
|
||||||
postgresql user: 'postgres'
|
postgresql user: 'postgres'
|
||||||
postgresql password: 'postgres'
|
postgresql password: 'postgres'
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from pretix.settings import *
|
from pretix.settings import *
|
||||||
|
|
||||||
LOGGING['handlers']['mail_admins']['include_html'] = True
|
LOGGING['handlers']['mail_admins']['include_html'] = True
|
||||||
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
|
STORAGES["staticfiles"]["BACKEND"] = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
|
||||||
|
|||||||
@@ -152,6 +152,10 @@ Example::
|
|||||||
password=abcd
|
password=abcd
|
||||||
host=localhost
|
host=localhost
|
||||||
port=3306
|
port=3306
|
||||||
|
sslmode=require
|
||||||
|
sslrootcert=/etc/pretix/postgresql-ca.crt
|
||||||
|
sslcert=/etc/pretix/postgresql-client-crt.crt
|
||||||
|
sslkey=/etc/pretix/postgresql-client-key.key
|
||||||
|
|
||||||
``backend``
|
``backend``
|
||||||
One of ``sqlite3`` and ``postgresql``.
|
One of ``sqlite3`` and ``postgresql``.
|
||||||
@@ -163,6 +167,11 @@ Example::
|
|||||||
``user``, ``password``, ``host``, ``port``
|
``user``, ``password``, ``host``, ``port``
|
||||||
Connection details for the database connection. Empty by default.
|
Connection details for the database connection. Empty by default.
|
||||||
|
|
||||||
|
``sslmode``, ``sslrootcert``
|
||||||
|
Connection TLS details for the PostgreSQL database connection. Possible values of ``sslmode`` are ``disable``, ``allow``, ``prefer``, ``require``, ``verify-ca``, and ``verify-full``. ``sslrootcert`` should be the accessible path of the ca certificate. Both values are empty by default.
|
||||||
|
|
||||||
|
``sslcert``, ``sslkey``
|
||||||
|
Connection mTLS details for the PostgreSQL database connection. It's also necessary to specify ``sslmode`` and ``sslrootcert`` parameters, please check the correct values from the TLS part. ``sslcert`` should be the accessible path of the client certificate. ``sslkey`` should be the accessible path of the client key. All values are empty by default.
|
||||||
.. _`config-replica`:
|
.. _`config-replica`:
|
||||||
|
|
||||||
Database replica settings
|
Database replica settings
|
||||||
@@ -324,6 +333,10 @@ to speed up various operations::
|
|||||||
["sentinel_host_3", 26379]
|
["sentinel_host_3", 26379]
|
||||||
]
|
]
|
||||||
password=password
|
password=password
|
||||||
|
ssl_cert_reqs=required
|
||||||
|
ssl_ca_certs=/etc/pretix/redis-ca.pem
|
||||||
|
ssl_keyfile=/etc/pretix/redis-client-crt.pem
|
||||||
|
ssl_certfile=/etc/pretix/redis-client-key.key
|
||||||
|
|
||||||
``location``
|
``location``
|
||||||
The location of redis, as a URL of the form ``redis://[:password]@localhost:6379/0``
|
The location of redis, as a URL of the form ``redis://[:password]@localhost:6379/0``
|
||||||
@@ -347,6 +360,22 @@ to speed up various operations::
|
|||||||
If your redis setup doesn't require a password or you already specified it in the location you can omit this option.
|
If your redis setup doesn't require a password or you already specified it in the location you can omit this option.
|
||||||
If this is set it will be passed to redis as the connection option PASSWORD.
|
If this is set it will be passed to redis as the connection option PASSWORD.
|
||||||
|
|
||||||
|
``ssl_cert_reqs``
|
||||||
|
If this is set it will be passed to redis as the connection option ``SSL_CERT_REQS``.
|
||||||
|
Possible values are ``none``, ``optional``, and ``required``.
|
||||||
|
|
||||||
|
``ssl_ca_certs``
|
||||||
|
If your redis setup doesn't require TLS you can omit this option.
|
||||||
|
If this is set it will be passed to redis as the connection option ``SSL_CA_CERTS``. Possible value is the ca path.
|
||||||
|
|
||||||
|
``ssl_keyfile``
|
||||||
|
If your redis setup doesn't require mTLS you can omit this option.
|
||||||
|
If this is set it will be passed to redis as the connection option ``SSL_KEYFILE``. Possible value is the keyfile path.
|
||||||
|
|
||||||
|
``ssl_certfile``
|
||||||
|
If your redis setup doesn't require mTLS you can omit this option.
|
||||||
|
If this is set it will be passed to redis as the connection option ``SSL_CERTFILE``. Possible value is the certfile path.
|
||||||
|
|
||||||
If redis is not configured, pretix will store sessions and locks in the database. If memcached
|
If redis is not configured, pretix will store sessions and locks in the database. If memcached
|
||||||
is configured, memcached will be used for caching instead of redis.
|
is configured, memcached will be used for caching instead of redis.
|
||||||
|
|
||||||
@@ -396,6 +425,8 @@ The two ``transport_options`` entries can be omitted in most cases.
|
|||||||
If they are present they need to be a valid JSON dictionary.
|
If they are present they need to be a valid JSON dictionary.
|
||||||
For possible entries in that dictionary see the `Celery documentation`_.
|
For possible entries in that dictionary see the `Celery documentation`_.
|
||||||
|
|
||||||
|
It is possible the use Redis with TLS/mTLS for the broker or the backend. To do so, it is necessary to specify the TLS identifier ``rediss``, the ssl mode ``ssl_cert_reqs`` and optionally specify the CA (TLS) ``ssl_ca_certs``, cert ``ssl_certfile`` and key ``ssl_keyfile`` (mTLS) path as encoded string. the following uri describes the format and possible parameters ``rediss://0.0.0.0:6379/1?ssl_cert_reqs=required&ssl_ca_certs=%2Fetc%2Fpretix%2Fredis-ca.pem&ssl_certfile=%2Fetc%2Fpretix%2Fredis-client-crt.pem&ssl_keyfile=%2Fetc%2Fpretix%2Fredis-client-key.key``
|
||||||
|
|
||||||
To use redis with sentinels set the broker or backend to ``sentinel://sentinel_host_1:26379;sentinel_host_2:26379/0``
|
To use redis with sentinels set the broker or backend to ``sentinel://sentinel_host_1:26379;sentinel_host_2:26379/0``
|
||||||
and the respective transport_options to ``{"master_name":"mymaster"}``.
|
and the respective transport_options to ``{"master_name":"mymaster"}``.
|
||||||
If your redis instances behind the sentinel have a password use ``sentinel://:my_password@sentinel_host_1:26379;sentinel_host_2:26379/0``.
|
If your redis instances behind the sentinel have a password use ``sentinel://:my_password@sentinel_host_1:26379;sentinel_host_2:26379/0``.
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ generated key and installs the plugin from the URL we told you::
|
|||||||
mkdir -p /etc/ssh && \
|
mkdir -p /etc/ssh && \
|
||||||
ssh-keyscan -t rsa -p 10022 code.rami.io >> /root/.ssh/known_hosts && \
|
ssh-keyscan -t rsa -p 10022 code.rami.io >> /root/.ssh/known_hosts && \
|
||||||
echo StrictHostKeyChecking=no >> /root/.ssh/config && \
|
echo StrictHostKeyChecking=no >> /root/.ssh/config && \
|
||||||
DJANGO_SETTINGS_MODULE=pretix.settings pip3 install -U "git+ssh://git@code.rami.io:10022/pretix/pretix-slack.git@stable#egg=pretix-slack" && \
|
DJANGO_SETTINGS_MODULE= pip3 install -U "git+ssh://git@code.rami.io:10022/pretix/pretix-slack.git@stable#egg=pretix-slack" && \
|
||||||
cd /pretix/src && \
|
cd /pretix/src && \
|
||||||
sudo -u pretixuser make production
|
sudo -u pretixuser make production
|
||||||
USER pretixuser
|
USER pretixuser
|
||||||
|
|||||||
+26
-2
@@ -35,9 +35,13 @@ as well as the type of underlying hardware. Example:
|
|||||||
"os_name": "Android",
|
"os_name": "Android",
|
||||||
"os_version": "2.3.6",
|
"os_version": "2.3.6",
|
||||||
"software_brand": "pretixdroid",
|
"software_brand": "pretixdroid",
|
||||||
"software_version": "4.0.0"
|
"software_version": "4.0.0",
|
||||||
|
"rsa_pubkey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqh…nswIDAQAB\n-----END PUBLIC KEY-----\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
The ``rsa_pubkey`` is optional any only required for certain fatures such as working with reusable
|
||||||
|
media and NFC cryptography.
|
||||||
|
|
||||||
Every initialization token can only be used once. On success, you will receive a response containing
|
Every initialization token can only be used once. On success, you will receive a response containing
|
||||||
information on your device as well as your API token:
|
information on your device as well as your API token:
|
||||||
|
|
||||||
@@ -137,9 +141,29 @@ The response will look like this:
|
|||||||
"id": 3,
|
"id": 3,
|
||||||
"name": "South entrance"
|
"name": "South entrance"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"server": {
|
||||||
|
"version": {
|
||||||
|
"pretix": "3.6.0.dev0",
|
||||||
|
"pretix_numeric": 30060001000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"medium_key_sets": [
|
||||||
|
{
|
||||||
|
"public_id": 3456349,
|
||||||
|
"organizer": "foo",
|
||||||
|
"active": true,
|
||||||
|
"media_type": "nfc_mf0aes",
|
||||||
|
"uid_key": "base64-encoded-encrypted-key",
|
||||||
|
"diversification_key": "base64-encoded-encrypted-key",
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
``"medium_key_sets`` will always be empty if you did not set an ``rsa_pubkey``.
|
||||||
|
The individual keys in the key sets are encrypted with the device's ``rsa_pubkey``
|
||||||
|
using ``RSA/ECB/PKCS1Padding``.
|
||||||
|
|
||||||
Creating a new API key
|
Creating a new API key
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ The reusable medium resource contains the following public fields:
|
|||||||
Field Type Description
|
Field Type Description
|
||||||
===================================== ========================== =======================================================
|
===================================== ========================== =======================================================
|
||||||
id integer Internal ID of the medium
|
id integer Internal ID of the medium
|
||||||
type string Type of medium, e.g. ``"barcode"`` or ``"nfc_uid"``.
|
type string Type of medium, e.g. ``"barcode"``, ``"nfc_uid"`` or ``"nfc_mf0aes"``.
|
||||||
organizer string Organizer slug of the organizer who "owns" this medium.
|
organizer string Organizer slug of the organizer who "owns" this medium.
|
||||||
identifier string Unique identifier of the medium. The format depends on the ``type``.
|
identifier string Unique identifier of the medium. The format depends on the ``type``.
|
||||||
active boolean Whether this medium may be used.
|
active boolean Whether this medium may be used.
|
||||||
@@ -37,6 +37,7 @@ Existing media types are:
|
|||||||
|
|
||||||
- ``barcode``
|
- ``barcode``
|
||||||
- ``nfc_uid``
|
- ``nfc_uid``
|
||||||
|
- ``nfc_mf0aes``
|
||||||
|
|
||||||
Endpoints
|
Endpoints
|
||||||
---------
|
---------
|
||||||
|
|||||||
@@ -18,8 +18,15 @@ subject multi-lingual string The subject of
|
|||||||
template multi-lingual string The body of the email
|
template multi-lingual string The body of the email
|
||||||
all_products boolean If ``true``, the email is sent to buyers of all products
|
all_products boolean If ``true``, the email is sent to buyers of all products
|
||||||
limit_products list of integers List of product IDs, if ``all_products`` is not set
|
limit_products list of integers List of product IDs, if ``all_products`` is not set
|
||||||
include_pending boolean If ``true``, the email is sent to pending orders. If ``false``,
|
[**DEPRECATED**] include_pending boolean If ``true``, the email is sent to pending orders. If ``false``,
|
||||||
only paid orders are considered.
|
only paid orders are considered.
|
||||||
|
restrict_to_status list List of order states to restrict recipients to. Valid
|
||||||
|
entries are ``p`` for paid, ``e`` for expired, ``c`` for canceled,
|
||||||
|
``n__pending_approval`` for pending approval,
|
||||||
|
``n__not_pending_approval_and_not_valid_if_pending`` for payment pending,
|
||||||
|
``n__valid_if_pending`` for payment pending but already confirmed,
|
||||||
|
and ``n__pending_overdue`` for pending with payment overdue.
|
||||||
|
The default is ``["p", "n__valid_if_pending"]``.
|
||||||
date_is_absolute boolean If ``true``, the email is set at a specific point in time.
|
date_is_absolute boolean If ``true``, the email is set at a specific point in time.
|
||||||
send_date datetime If ``date_is_absolute`` is set: Date and time to send the email.
|
send_date datetime If ``date_is_absolute`` is set: Date and time to send the email.
|
||||||
send_offset_days integer If ``date_is_absolute`` is not set, this is the number of days
|
send_offset_days integer If ``date_is_absolute`` is not set, this is the number of days
|
||||||
@@ -37,7 +44,10 @@ send_to string Can be ``"order
|
|||||||
or ``"both"``.
|
or ``"both"``.
|
||||||
date. Otherwise it is relative to the event start date.
|
date. Otherwise it is relative to the event start date.
|
||||||
===================================== ========================== =======================================================
|
===================================== ========================== =======================================================
|
||||||
|
.. versionchanged:: 2023.7
|
||||||
|
|
||||||
|
The ``include_pending`` field has been deprecated.
|
||||||
|
The ``restrict_to_status`` field has been added.
|
||||||
|
|
||||||
Endpoints
|
Endpoints
|
||||||
---------
|
---------
|
||||||
@@ -74,7 +84,11 @@ Endpoints
|
|||||||
"template": {"en": "Don't forget your tickets, download them at {url}"},
|
"template": {"en": "Don't forget your tickets, download them at {url}"},
|
||||||
"all_products": true,
|
"all_products": true,
|
||||||
"limit_products": [],
|
"limit_products": [],
|
||||||
"include_pending": false,
|
"restrict_to_status": [
|
||||||
|
"p",
|
||||||
|
"n__not_pending_approval_and_not_valid_if_pending",
|
||||||
|
"n__valid_if_pending"
|
||||||
|
],
|
||||||
"send_date": null,
|
"send_date": null,
|
||||||
"send_offset_days": 1,
|
"send_offset_days": 1,
|
||||||
"send_offset_time": "18:00",
|
"send_offset_time": "18:00",
|
||||||
@@ -120,7 +134,11 @@ Endpoints
|
|||||||
"template": {"en": "Don't forget your tickets, download them at {url}"},
|
"template": {"en": "Don't forget your tickets, download them at {url}"},
|
||||||
"all_products": true,
|
"all_products": true,
|
||||||
"limit_products": [],
|
"limit_products": [],
|
||||||
"include_pending": false,
|
"restrict_to_status": [
|
||||||
|
"p",
|
||||||
|
"n__not_pending_approval_and_not_valid_if_pending",
|
||||||
|
"n__valid_if_pending"
|
||||||
|
],
|
||||||
"send_date": null,
|
"send_date": null,
|
||||||
"send_offset_days": 1,
|
"send_offset_days": 1,
|
||||||
"send_offset_time": "18:00",
|
"send_offset_time": "18:00",
|
||||||
@@ -157,7 +175,11 @@ Endpoints
|
|||||||
"template": {"en": "Don't forget your tickets, download them at {url}"},
|
"template": {"en": "Don't forget your tickets, download them at {url}"},
|
||||||
"all_products": true,
|
"all_products": true,
|
||||||
"limit_products": [],
|
"limit_products": [],
|
||||||
"include_pending": false,
|
"restrict_to_status": [
|
||||||
|
"p",
|
||||||
|
"n__not_pending_approval_and_not_valid_if_pending",
|
||||||
|
"n__valid_if_pending"
|
||||||
|
],
|
||||||
"send_date": null,
|
"send_date": null,
|
||||||
"send_offset_days": 1,
|
"send_offset_days": 1,
|
||||||
"send_offset_time": "18:00",
|
"send_offset_time": "18:00",
|
||||||
@@ -182,7 +204,11 @@ Endpoints
|
|||||||
"template": {"en": "Don't forget your tickets, download them at {url}"},
|
"template": {"en": "Don't forget your tickets, download them at {url}"},
|
||||||
"all_products": true,
|
"all_products": true,
|
||||||
"limit_products": [],
|
"limit_products": [],
|
||||||
"include_pending": false,
|
"restrict_to_status": [
|
||||||
|
"p",
|
||||||
|
"n__not_pending_approval_and_not_valid_if_pending",
|
||||||
|
"n__valid_if_pending"
|
||||||
|
],
|
||||||
"send_date": null,
|
"send_date": null,
|
||||||
"send_offset_days": 1,
|
"send_offset_days": 1,
|
||||||
"send_offset_time": "18:00",
|
"send_offset_time": "18:00",
|
||||||
@@ -235,7 +261,11 @@ Endpoints
|
|||||||
"template": {"en": "Don't forget your tickets, download them at {url}"},
|
"template": {"en": "Don't forget your tickets, download them at {url}"},
|
||||||
"all_products": true,
|
"all_products": true,
|
||||||
"limit_products": [],
|
"limit_products": [],
|
||||||
"include_pending": false,
|
"restrict_to_status": [
|
||||||
|
"p",
|
||||||
|
"n__not_pending_approval_and_not_valid_if_pending",
|
||||||
|
"n__valid_if_pending"
|
||||||
|
],
|
||||||
"send_date": null,
|
"send_date": null,
|
||||||
"send_offset_days": 1,
|
"send_offset_days": 1,
|
||||||
"send_offset_time": "18:00",
|
"send_offset_time": "18:00",
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ Backend
|
|||||||
item_formsets, order_search_filter_q, order_search_forms
|
item_formsets, order_search_filter_q, order_search_forms
|
||||||
|
|
||||||
.. automodule:: pretix.base.signals
|
.. automodule:: pretix.base.signals
|
||||||
:members: logentry_display, logentry_object_link, requiredaction_display, timeline_events, orderposition_blocked_display
|
:members: logentry_display, logentry_object_link, requiredaction_display, timeline_events, orderposition_blocked_display, customer_created, customer_signed_in
|
||||||
|
|
||||||
Vouchers
|
Vouchers
|
||||||
""""""""
|
""""""""
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ you to execute a piece of code with a different locale:
|
|||||||
This is very useful e.g. when sending an email to a user that has a different language than the user performing the
|
This is very useful e.g. when sending an email to a user that has a different language than the user performing the
|
||||||
action that causes the mail to be sent.
|
action that causes the mail to be sent.
|
||||||
|
|
||||||
.. _translation features: https://docs.djangoproject.com/en/1.9/topics/i18n/translation/
|
.. _translation features: https://docs.djangoproject.com/en/4.2/topics/i18n/translation/
|
||||||
.. _GNU gettext: https://www.gnu.org/software/gettext/
|
.. _GNU gettext: https://www.gnu.org/software/gettext/
|
||||||
.. _strings: https://django-i18nfield.readthedocs.io/en/latest/strings.html
|
.. _strings: https://django-i18nfield.readthedocs.io/en/latest/strings.html
|
||||||
.. _database fields: https://django-i18nfield.readthedocs.io/en/latest/quickstart.html
|
.. _database fields: https://django-i18nfield.readthedocs.io/en/latest/quickstart.html
|
||||||
|
|||||||
@@ -15,25 +15,33 @@ and the admin panel is available at ``https://pretix.eu/control/event/bigorg/awe
|
|||||||
|
|
||||||
If the organizer now configures a custom domain like ``tickets.bigorg.com``, his event will
|
If the organizer now configures a custom domain like ``tickets.bigorg.com``, his event will
|
||||||
from now on be available on ``https://tickets.bigorg.com/awesomecon/``. The former URL at
|
from now on be available on ``https://tickets.bigorg.com/awesomecon/``. The former URL at
|
||||||
``pretix.eu`` will redirect there. However, the admin panel will still only be available
|
``pretix.eu`` will redirect there. It's also possible to do this for just an event, in which
|
||||||
on ``pretix.eu`` for convenience and security reasons.
|
case the event will be available on ``https://tickets.awesomecon.org/``.
|
||||||
|
|
||||||
|
However, the admin panel will still only be available on ``pretix.eu`` for convenience and security reasons.
|
||||||
|
|
||||||
URL routing
|
URL routing
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
The hard part about implementing this URL routing in Django is that
|
The hard part about implementing this URL routing in Django is that
|
||||||
``https://pretix.eu/bigorg/awesomecon/`` contains two parameters of nearly arbitrary content
|
``https://pretix.eu/bigorg/awesomecon/`` contains two parameters of nearly arbitrary content
|
||||||
and ``https://tickets.bigorg.com/awesomecon/`` contains only one. The only robust way to do
|
and ``https://tickets.bigorg.com/awesomecon/`` contains only one and ``https://tickets.awesomecon.org/`` does not contain any.
|
||||||
this is by having *separate* URL configuration for those two cases. In pretix, we call the
|
The only robust way to do this is by having *separate* URL configuration for those three cases.
|
||||||
former our ``maindomain`` config and the latter our ``subdomain`` config. For pretix's core
|
|
||||||
modules we do some magic to avoid duplicate configuration, but for a fairly simple plugin with
|
|
||||||
only a handful of routes, we recommend just configuring the two URL sets separately.
|
|
||||||
|
|
||||||
|
In pretix, we therefore do not have a global URL configuration, but three, living in the following modules:
|
||||||
|
|
||||||
|
- ``pretix.multidomain.maindomain_urlconf``
|
||||||
|
- ``pretix.multidomain.organizer_domain_urlconf``
|
||||||
|
- ``pretix.multidomain.event_domain_urlconf``
|
||||||
|
|
||||||
|
We provide some helper utilities to work with these to avoid duplicate configuration of the individual URLs.
|
||||||
The file ``urls.py`` inside your plugin package will be loaded and scanned for URL configuration
|
The file ``urls.py`` inside your plugin package will be loaded and scanned for URL configuration
|
||||||
automatically and should be provided by any plugin that provides any view.
|
automatically and should be provided by any plugin that provides any view.
|
||||||
|
However, unlike plain Django, we look not only for a ``urlpatterns`` attribute on the module but support other
|
||||||
|
attributes like ``event_patterns`` and ``organizer_patterns`` as well.
|
||||||
|
|
||||||
A very basic example that provides one view in the admin panel and one view in the frontend
|
For example, for a simple plugin that adds one URL to the backend and one event-level URL to the frontend, you can
|
||||||
could look like this::
|
create the following configuration in your ``urls.py``::
|
||||||
|
|
||||||
from django.urls import re_path
|
from django.urls import re_path
|
||||||
|
|
||||||
@@ -52,7 +60,7 @@ could look like this::
|
|||||||
As you can see, the view in the frontend is not included in the standard Django ``urlpatterns``
|
As you can see, the view in the frontend is not included in the standard Django ``urlpatterns``
|
||||||
setting but in a separate list with the name ``event_patterns``. This will automatically prepend
|
setting but in a separate list with the name ``event_patterns``. This will automatically prepend
|
||||||
the appropriate parameters to the regex (e.g. the event or the event and the organizer, depending
|
the appropriate parameters to the regex (e.g. the event or the event and the organizer, depending
|
||||||
on the called domain).
|
on the called domain). For organizer-level views, ``organizer_patterns`` works the same way.
|
||||||
|
|
||||||
If you only provide URLs in the admin area, you do not need to provide a ``event_patterns`` attribute.
|
If you only provide URLs in the admin area, you do not need to provide a ``event_patterns`` attribute.
|
||||||
|
|
||||||
@@ -71,11 +79,16 @@ is a python method that emulates a behavior similar to ``reverse``:
|
|||||||
|
|
||||||
.. autofunction:: pretix.multidomain.urlreverse.eventreverse
|
.. autofunction:: pretix.multidomain.urlreverse.eventreverse
|
||||||
|
|
||||||
|
If you need to communicate the URL externally, you can use a different method to ensure that it is always an absolute URL:
|
||||||
|
|
||||||
|
.. autofunction:: pretix.multidomain.urlreverse.build_absolute_uri
|
||||||
|
|
||||||
In addition, there is a template tag that works similar to ``url`` but takes an event or organizer object
|
In addition, there is a template tag that works similar to ``url`` but takes an event or organizer object
|
||||||
as its first argument and can be used like this::
|
as its first argument and can be used like this::
|
||||||
|
|
||||||
{% load eventurl %}
|
{% load eventurl %}
|
||||||
<a href="{% eventurl request.event "presale:event.checkout" step="payment" %}">Pay</a>
|
<a href="{% eventurl request.event "presale:event.checkout" step="payment" %}">Pay</a>
|
||||||
|
<a href="{% abseventurl request.event "presale:event.checkout" step="payment" %}">Pay</a>
|
||||||
|
|
||||||
|
|
||||||
Implementation details
|
Implementation details
|
||||||
|
|||||||
@@ -12,3 +12,4 @@ Developer documentation
|
|||||||
api/index
|
api/index
|
||||||
structure
|
structure
|
||||||
translation/index
|
translation/index
|
||||||
|
nfc/index
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
NFC media
|
||||||
|
=========
|
||||||
|
|
||||||
|
pretix supports using NFC chips as "reusable media", for example to store gift cards or tickets.
|
||||||
|
|
||||||
|
Most of this implementation currently lives in our proprietary app pretixPOS, but in the future might also become part of our open-source pretixSCAN solution.
|
||||||
|
Either way, we want this to be an open ecosystem and therefore document the exact mechanisms in use on the following pages.
|
||||||
|
|
||||||
|
We support multiple implementations of NFC media, each documented on its own page:
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
uid
|
||||||
|
mf0aes
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
Mifare Ultralight AES
|
||||||
|
=====================
|
||||||
|
|
||||||
|
We offer an implementation that provides a higher security level than the UID-based approach and uses the `Mifare Ultralight AES`_ chip sold by NXP.
|
||||||
|
We believe the security model of this approach is adequate to the situation where this will usually be used and we'll outline known risks below.
|
||||||
|
|
||||||
|
If you want to dive deeper into the properties of the Mifare Ultralight AES chip, we recommend reading the `data sheet`_.
|
||||||
|
|
||||||
|
Random UIDs
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Mifare Ultralight AES supports a feature that returns a randomized UID every time a non-authenticated user tries to
|
||||||
|
read the UID. This has a strong privacy benefit, since no unauthorized entity can use the NFC chips to track users.
|
||||||
|
On the other hand, this reduces interoperability of the system. For example, this prevents you from using the same NFC
|
||||||
|
chips for a different purpose where you only need the UID. This will also prevent your guests from reading their UID
|
||||||
|
themselves with their phones, which might be useful e.g. in debugging situations.
|
||||||
|
|
||||||
|
Since there's no one-size-fits-all choice here, you can enable or disable this feature in the pretix organizer
|
||||||
|
settings. If you change it, the change will apply to all newly encoded chips after the change.
|
||||||
|
|
||||||
|
Key management
|
||||||
|
--------------
|
||||||
|
|
||||||
|
For every organizer, the server will generate create a "key set", which consists of a publicly known ID (random 32-bit integer) and two 16-byte keys ("diversification key" and "UID key").
|
||||||
|
|
||||||
|
Using our :ref:`Device authentication mechanism <rest-deviceauth>`, an authorized device can submit a locally generated RSA public key to the server.
|
||||||
|
This key can no longer changed on the server once it is set, thus protecting against the attack scenario of a leaked device API token.
|
||||||
|
|
||||||
|
The server will then include key sets in the response to ``/api/v1/device/info``, encrypted with the device's RSA key.
|
||||||
|
This includes all key sets generated for the organizer the device belongs to, as well as all keys of organizers that have granted sufficient access to this organizer.
|
||||||
|
|
||||||
|
The device will decrypt the key sets using its RSA key and store the key sets locally.
|
||||||
|
|
||||||
|
.. warning:: The device **will** have access to the raw key sets. Therefore, there is a risk of leaked master keys if an
|
||||||
|
authorized device is stolen or abused. Our implementation in pretixPOS attempts to make this very hard on
|
||||||
|
modern, non-rooted Android devices by keeping them encrypted with the RSA key and only storing the RSA key
|
||||||
|
in the hardware-backed keystore of the device. A sufficiently motivated attacker, however, will likely still
|
||||||
|
be able to extract the keys from a stolen device.
|
||||||
|
|
||||||
|
Encoding a chip
|
||||||
|
---------------
|
||||||
|
|
||||||
|
When a new chip is encoded, the following steps will be taken:
|
||||||
|
|
||||||
|
- The UID of the chip is retrieved.
|
||||||
|
|
||||||
|
- A chip-specific key is generated using the mechanism documented in `AN10922`_ using the "diversification key" from the
|
||||||
|
organizer's key set as the CMAC key and the diversification input concatenated in the from of ``0x01 + UID + APPID + SYSTEMID``
|
||||||
|
with the following values:
|
||||||
|
|
||||||
|
- The UID of the chip as ``UID``
|
||||||
|
|
||||||
|
- ``"eu.pretix"`` (``0x65 0x75 0x2e 0x70 0x72 0x65 0x74 0x69 0x78``) as ``APPID``
|
||||||
|
|
||||||
|
- The ``public_id`` from the organizer's key set as a 4-byte big-endian value as ``SYSTEMID``
|
||||||
|
|
||||||
|
- The chip-specific key is written to the chip as the "data protection key" (config pages 0x30 to 0x33)
|
||||||
|
|
||||||
|
- The UID key from the organizer's key set is written to the chip as the "UID retrieval key" (config pages 0x34 to 0x37)
|
||||||
|
|
||||||
|
- The config page 0x29 is set like this:
|
||||||
|
|
||||||
|
- ``RID_ACT`` (random UID) to ``1`` or ``0`` based on the organizer's configuration
|
||||||
|
- ``SEC_MSG_ACT`` (secure messaging) to ``1``
|
||||||
|
- ``AUTH0`` (first page that needs authentication) to 0x04 (first non-UID page)
|
||||||
|
|
||||||
|
- The config page 0x2A is set like this:
|
||||||
|
|
||||||
|
- ``PROT`` to ``0`` (only write access restricted, not read access)
|
||||||
|
- ``AUTHLIM`` to ``256`` (maximum number of wrong authentications before "self-desctruction")
|
||||||
|
- Everything else to its default value (no lock bits are set)
|
||||||
|
|
||||||
|
- The ``public_id`` of the key set will be written to page 0x04 as a big-endian value
|
||||||
|
|
||||||
|
- The UID of the chip will be registered as a reusable medium on the server.
|
||||||
|
|
||||||
|
.. warning:: During encoding, the chip-specific key and the UID key are transmitted in plain text over the air. The
|
||||||
|
security model therefore relies on the encoding of chips being performed in a trusted physical environment
|
||||||
|
to prevent a nearby attacker from sniffing the keys with a strong antenna.
|
||||||
|
|
||||||
|
.. note:: If an attacker tries to authenticate with the chip 256 times using the wrong key, the chip will become
|
||||||
|
unusable. A chip may also become unusable if it is detached from the reader in the middle of the encoding
|
||||||
|
process (even though we've tried to implement it in a way that makes this unlikely).
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
When a chip is presented to the NFC reader, the following steps will be taken:
|
||||||
|
|
||||||
|
- Command ``GET_VERSION`` is used to determine if it is a Mifare Ultralight AES chip (if not, abort).
|
||||||
|
|
||||||
|
- Page 0x04 is read. If it is all zeroes, the chip is considered un-encoded (abort). If it contains a value that
|
||||||
|
corresponds to the ``public_id`` of a known key set, this key set is used for all further operations. If it contains
|
||||||
|
a different value, we consider this chip to belong to a different organizer or not to a pretix system at all (abort).
|
||||||
|
|
||||||
|
- An authentication with the chip using the UID key is performed.
|
||||||
|
|
||||||
|
- The UID of the chip will be read.
|
||||||
|
|
||||||
|
- The chip-specific key will be derived using the mechanism described above in the encoding step.
|
||||||
|
|
||||||
|
- An authentication with the chip using the chip-specific key is performed. If this is fully successful, this step
|
||||||
|
proves that the chip knows the same chip-specific key as we do and is therefore an authentic chip encoded by us and
|
||||||
|
we can trust its UID value.
|
||||||
|
|
||||||
|
- The UID is transmitted to the server to fetch the correct medium.
|
||||||
|
|
||||||
|
During these steps, the keys are never transmitted in plain text and can thus not be sniffed by a nearby attacker
|
||||||
|
with a strong antenna.
|
||||||
|
|
||||||
|
.. _Mifare Ultralight AES: https://www.nxp.com/products/rfid-nfc/mifare-hf/mifare-ultralight/mifare-ultralight-aes-enhanced-security-for-limited-use-contactless-applications:MF0AESx20
|
||||||
|
.. _data sheet: https://www.nxp.com/docs/en/data-sheet/MF0AES(H)20.pdf
|
||||||
|
.. _AN10922: https://www.nxp.com/docs/en/application-note/AN10922.pdf
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
UID-based
|
||||||
|
=========
|
||||||
|
|
||||||
|
With UID-based NFC, only the unique ID (UID) of the NFC chip is used for identification purposes.
|
||||||
|
This can be used with virtually all NFC chips that provide compatibility with the NFC reader in use, typically at least all chips that comply with ISO/IEC 14443-3A.
|
||||||
|
|
||||||
|
We make only one restriction: The UID may not start with ``08``, since that usually signifies a randomized UID that changes on every read (which would not be very useful).
|
||||||
|
|
||||||
|
.. warning:: The UID-based approach provides only a very low level of security. It is easy to clone a chip with the same
|
||||||
|
UID and impersonate someone else.
|
||||||
@@ -96,6 +96,20 @@ http://localhost:8000/control/ for the admin view.
|
|||||||
port (for example because you develop on `pretixdroid`_), you can check
|
port (for example because you develop on `pretixdroid`_), you can check
|
||||||
`Django's documentation`_ for more options.
|
`Django's documentation`_ for more options.
|
||||||
|
|
||||||
|
When running the local development webserver, ensure Celery is not configured
|
||||||
|
in ``pretix.cfg``. i.e., you should remove anything such as::
|
||||||
|
|
||||||
|
[celery]
|
||||||
|
backend=redis://redis:6379/2
|
||||||
|
broker=redis://redis:6379/2
|
||||||
|
|
||||||
|
If you choose to use Celery for development, you must also start a Celery worker
|
||||||
|
process::
|
||||||
|
|
||||||
|
celery -A pretix.celery_app worker -l info
|
||||||
|
|
||||||
|
However, beware that code changes will not auto-reload within Celery.
|
||||||
|
|
||||||
.. _`checksandtests`:
|
.. _`checksandtests`:
|
||||||
|
|
||||||
Code checks and unit tests
|
Code checks and unit tests
|
||||||
|
|||||||
+3
-2
@@ -36,7 +36,7 @@ dependencies = [
|
|||||||
"css-inline==0.8.*",
|
"css-inline==0.8.*",
|
||||||
"defusedcsv>=1.1.0",
|
"defusedcsv>=1.1.0",
|
||||||
"dj-static",
|
"dj-static",
|
||||||
"Django==4.1.*",
|
"Django==4.2.*",
|
||||||
"django-bootstrap3==23.1.*",
|
"django-bootstrap3==23.1.*",
|
||||||
"django-compressor==4.3.*",
|
"django-compressor==4.3.*",
|
||||||
"django-countries==7.5.*",
|
"django-countries==7.5.*",
|
||||||
@@ -90,7 +90,7 @@ dependencies = [
|
|||||||
"pytz-deprecation-shim==0.1.*",
|
"pytz-deprecation-shim==0.1.*",
|
||||||
"pyuca",
|
"pyuca",
|
||||||
"qrcode==7.4.*",
|
"qrcode==7.4.*",
|
||||||
"redis==4.5.*,>=4.5.4",
|
"redis==4.6.*",
|
||||||
"reportlab==4.0.*",
|
"reportlab==4.0.*",
|
||||||
"requests==2.31.*",
|
"requests==2.31.*",
|
||||||
"sentry-sdk==1.15.*",
|
"sentry-sdk==1.15.*",
|
||||||
@@ -112,6 +112,7 @@ memcached = ["pylibmc"]
|
|||||||
dev = [
|
dev = [
|
||||||
"coverage",
|
"coverage",
|
||||||
"coveralls",
|
"coveralls",
|
||||||
|
"fakeredis==2.18.*",
|
||||||
"flake8==6.0.*",
|
"flake8==6.0.*",
|
||||||
"freezegun",
|
"freezegun",
|
||||||
"isort==5.12.*",
|
"isort==5.12.*",
|
||||||
|
|||||||
@@ -19,4 +19,4 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
# 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/>.
|
# <https://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
__version__ = "2023.7.0.dev0"
|
__version__ = "2023.8.0.dev0"
|
||||||
|
|||||||
@@ -196,7 +196,14 @@ STATICFILES_DIRS = [
|
|||||||
|
|
||||||
STATICI18N_ROOT = os.path.join(BASE_DIR, "pretix/static")
|
STATICI18N_ROOT = os.path.join(BASE_DIR, "pretix/static")
|
||||||
|
|
||||||
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
|
STORAGES = {
|
||||||
|
"default": {
|
||||||
|
"BACKEND": "django.core.files.storage.FileSystemStorage",
|
||||||
|
},
|
||||||
|
"staticfiles": {
|
||||||
|
"BACKEND": "django.contrib.staticfiles.storage.ManifestStaticFilesStorage",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
# if os.path.exists(os.path.join(DATA_DIR, 'static')):
|
# if os.path.exists(os.path.join(DATA_DIR, 'static')):
|
||||||
# STATICFILES_DIRS.insert(0, os.path.join(DATA_DIR, 'static'))
|
# STATICFILES_DIRS.insert(0, os.path.join(DATA_DIR, 'static'))
|
||||||
|
|||||||
@@ -223,6 +223,7 @@ class PretixPosSecurityProfile(AllowListSecurityProfile):
|
|||||||
('POST', 'api-v1:checkinrpc.redeem'),
|
('POST', 'api-v1:checkinrpc.redeem'),
|
||||||
('GET', 'api-v1:checkinrpc.search'),
|
('GET', 'api-v1:checkinrpc.search'),
|
||||||
('POST', 'api-v1:reusablemedium-lookup'),
|
('POST', 'api-v1:reusablemedium-lookup'),
|
||||||
|
('POST', 'api-v1:reusablemedium-list'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -817,6 +817,10 @@ class EventSettingsSerializer(SettingsSerializer):
|
|||||||
'reusable_media_type_nfc_uid',
|
'reusable_media_type_nfc_uid',
|
||||||
'reusable_media_type_nfc_uid_autocreate_giftcard',
|
'reusable_media_type_nfc_uid_autocreate_giftcard',
|
||||||
'reusable_media_type_nfc_uid_autocreate_giftcard_currency',
|
'reusable_media_type_nfc_uid_autocreate_giftcard_currency',
|
||||||
|
'reusable_media_type_nfc_mf0aes',
|
||||||
|
'reusable_media_type_nfc_mf0aes_autocreate_giftcard',
|
||||||
|
'reusable_media_type_nfc_mf0aes_autocreate_giftcard_currency',
|
||||||
|
'reusable_media_type_nfc_mf0aes_random_uid',
|
||||||
]
|
]
|
||||||
readonly_fields = [
|
readonly_fields = [
|
||||||
# These are read-only since they are currently only settable on organizers, not events
|
# These are read-only since they are currently only settable on organizers, not events
|
||||||
@@ -826,6 +830,10 @@ class EventSettingsSerializer(SettingsSerializer):
|
|||||||
'reusable_media_type_nfc_uid',
|
'reusable_media_type_nfc_uid',
|
||||||
'reusable_media_type_nfc_uid_autocreate_giftcard',
|
'reusable_media_type_nfc_uid_autocreate_giftcard',
|
||||||
'reusable_media_type_nfc_uid_autocreate_giftcard_currency',
|
'reusable_media_type_nfc_uid_autocreate_giftcard_currency',
|
||||||
|
'reusable_media_type_nfc_mf0aes',
|
||||||
|
'reusable_media_type_nfc_mf0aes_autocreate_giftcard',
|
||||||
|
'reusable_media_type_nfc_mf0aes_autocreate_giftcard_currency',
|
||||||
|
'reusable_media_type_nfc_mf0aes_random_uid',
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@@ -894,6 +902,8 @@ class DeviceEventSettingsSerializer(EventSettingsSerializer):
|
|||||||
'name_scheme',
|
'name_scheme',
|
||||||
'reusable_media_type_barcode',
|
'reusable_media_type_barcode',
|
||||||
'reusable_media_type_nfc_uid',
|
'reusable_media_type_nfc_uid',
|
||||||
|
'reusable_media_type_nfc_mf0aes',
|
||||||
|
'reusable_media_type_nfc_mf0aes_random_uid',
|
||||||
'system_question_order',
|
'system_question_order',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ from decimal import Decimal
|
|||||||
import pycountry
|
import pycountry
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.files import File
|
from django.core.files import File
|
||||||
|
from django.db import models
|
||||||
from django.db.models import F, Q
|
from django.db.models import F, Q
|
||||||
from django.utils.encoding import force_str
|
from django.utils.encoding import force_str
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
@@ -372,11 +373,15 @@ class PdfDataSerializer(serializers.Field):
|
|||||||
self.context['vars_images'] = get_images(self.context['event'])
|
self.context['vars_images'] = get_images(self.context['event'])
|
||||||
|
|
||||||
for k, f in self.context['vars'].items():
|
for k, f in self.context['vars'].items():
|
||||||
try:
|
if 'evaluate_bulk' in f:
|
||||||
res[k] = f['evaluate'](instance, instance.order, ev)
|
# Will be evaluated later by our list serializers
|
||||||
except:
|
res[k] = (f['evaluate_bulk'], instance)
|
||||||
logger.exception('Evaluating PDF variable failed')
|
else:
|
||||||
res[k] = '(error)'
|
try:
|
||||||
|
res[k] = f['evaluate'](instance, instance.order, ev)
|
||||||
|
except:
|
||||||
|
logger.exception('Evaluating PDF variable failed')
|
||||||
|
res[k] = '(error)'
|
||||||
|
|
||||||
if not hasattr(ev, '_cached_meta_data'):
|
if not hasattr(ev, '_cached_meta_data'):
|
||||||
ev._cached_meta_data = ev.meta_data
|
ev._cached_meta_data = ev.meta_data
|
||||||
@@ -429,6 +434,38 @@ class PdfDataSerializer(serializers.Field):
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
class OrderPositionListSerializer(serializers.ListSerializer):
|
||||||
|
|
||||||
|
def to_representation(self, data):
|
||||||
|
# We have a custom implementation of this method because PdfDataSerializer() might keep some elements unevaluated
|
||||||
|
# with a (callable, input) tuple. We'll loop over these entries and evaluate them bulk-wise to save on SQL queries.
|
||||||
|
|
||||||
|
if isinstance(self.parent, OrderSerializer) and isinstance(self.parent.parent, OrderListSerializer):
|
||||||
|
# Do not execute our custom code because it will be executed by OrderListSerializer later for the
|
||||||
|
# full result set.
|
||||||
|
return super().to_representation(data)
|
||||||
|
|
||||||
|
iterable = data.all() if isinstance(data, models.Manager) else data
|
||||||
|
|
||||||
|
data = []
|
||||||
|
evaluate_queue = defaultdict(list)
|
||||||
|
|
||||||
|
for item in iterable:
|
||||||
|
entry = self.child.to_representation(item)
|
||||||
|
if "pdf_data" in entry:
|
||||||
|
for k, v in entry["pdf_data"].items():
|
||||||
|
if isinstance(v, tuple) and callable(v[0]):
|
||||||
|
evaluate_queue[v[0]].append((v[1], entry, k))
|
||||||
|
data.append(entry)
|
||||||
|
|
||||||
|
for func, entries in evaluate_queue.items():
|
||||||
|
results = func([item for (item, entry, k) in entries])
|
||||||
|
for (item, entry, k), result in zip(entries, results):
|
||||||
|
entry["pdf_data"][k] = result
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
class OrderPositionSerializer(I18nAwareModelSerializer):
|
class OrderPositionSerializer(I18nAwareModelSerializer):
|
||||||
checkins = CheckinSerializer(many=True, read_only=True)
|
checkins = CheckinSerializer(many=True, read_only=True)
|
||||||
answers = AnswerSerializer(many=True)
|
answers = AnswerSerializer(many=True)
|
||||||
@@ -440,6 +477,7 @@ class OrderPositionSerializer(I18nAwareModelSerializer):
|
|||||||
attendee_name = serializers.CharField(required=False)
|
attendee_name = serializers.CharField(required=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
list_serializer_class = OrderPositionListSerializer
|
||||||
model = OrderPosition
|
model = OrderPosition
|
||||||
fields = ('id', 'order', 'positionid', 'item', 'variation', 'price', 'attendee_name', 'attendee_name_parts',
|
fields = ('id', 'order', 'positionid', 'item', 'variation', 'price', 'attendee_name', 'attendee_name_parts',
|
||||||
'company', 'street', 'zipcode', 'city', 'country', 'state', 'discount',
|
'company', 'street', 'zipcode', 'city', 'country', 'state', 'discount',
|
||||||
@@ -468,6 +506,20 @@ class OrderPositionSerializer(I18nAwareModelSerializer):
|
|||||||
def validate(self, data):
|
def validate(self, data):
|
||||||
raise TypeError("this serializer is readonly")
|
raise TypeError("this serializer is readonly")
|
||||||
|
|
||||||
|
def to_representation(self, data):
|
||||||
|
if isinstance(self.parent, (OrderListSerializer, OrderPositionListSerializer)):
|
||||||
|
# Do not execute our custom code because it will be executed by OrderListSerializer later for the
|
||||||
|
# full result set.
|
||||||
|
return super().to_representation(data)
|
||||||
|
|
||||||
|
entry = super().to_representation(data)
|
||||||
|
if "pdf_data" in entry:
|
||||||
|
for k, v in entry["pdf_data"].items():
|
||||||
|
if isinstance(v, tuple) and callable(v[0]):
|
||||||
|
entry["pdf_data"][k] = v[0]([v[1]])[0]
|
||||||
|
|
||||||
|
return entry
|
||||||
|
|
||||||
|
|
||||||
class RequireAttentionField(serializers.Field):
|
class RequireAttentionField(serializers.Field):
|
||||||
def to_representation(self, instance: OrderPosition):
|
def to_representation(self, instance: OrderPosition):
|
||||||
@@ -613,6 +665,34 @@ class OrderURLField(serializers.URLField):
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
class OrderListSerializer(serializers.ListSerializer):
|
||||||
|
|
||||||
|
def to_representation(self, data):
|
||||||
|
# We have a custom implementation of this method because PdfDataSerializer() might keep some elements
|
||||||
|
# unevaluated with a (callable, input) tuple. We'll loop over these entries and evaluate them bulk-wise to
|
||||||
|
# save on SQL queries.
|
||||||
|
iterable = data.all() if isinstance(data, models.Manager) else data
|
||||||
|
|
||||||
|
data = []
|
||||||
|
evaluate_queue = defaultdict(list)
|
||||||
|
|
||||||
|
for item in iterable:
|
||||||
|
entry = self.child.to_representation(item)
|
||||||
|
for p in entry.get("positions", []):
|
||||||
|
if "pdf_data" in p:
|
||||||
|
for k, v in p["pdf_data"].items():
|
||||||
|
if isinstance(v, tuple) and callable(v[0]):
|
||||||
|
evaluate_queue[v[0]].append((v[1], p, k))
|
||||||
|
data.append(entry)
|
||||||
|
|
||||||
|
for func, entries in evaluate_queue.items():
|
||||||
|
results = func([item for (item, entry, k) in entries])
|
||||||
|
for (item, entry, k), result in zip(entries, results):
|
||||||
|
entry["pdf_data"][k] = result
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
class OrderSerializer(I18nAwareModelSerializer):
|
class OrderSerializer(I18nAwareModelSerializer):
|
||||||
invoice_address = InvoiceAddressSerializer(allow_null=True)
|
invoice_address = InvoiceAddressSerializer(allow_null=True)
|
||||||
positions = OrderPositionSerializer(many=True, read_only=True)
|
positions = OrderPositionSerializer(many=True, read_only=True)
|
||||||
@@ -627,6 +707,7 @@ class OrderSerializer(I18nAwareModelSerializer):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Order
|
model = Order
|
||||||
|
list_serializer_class = OrderListSerializer
|
||||||
fields = (
|
fields = (
|
||||||
'code', 'status', 'testmode', 'secret', 'email', 'phone', 'locale', 'datetime', 'expires', 'payment_date',
|
'code', 'status', 'testmode', 'secret', 'email', 'phone', 'locale', 'datetime', 'expires', 'payment_date',
|
||||||
'payment_provider', 'fees', 'total', 'comment', 'custom_followup_at', 'invoice_address', 'positions', 'downloads',
|
'payment_provider', 'fees', 'total', 'comment', 'custom_followup_at', 'invoice_address', 'positions', 'downloads',
|
||||||
|
|||||||
@@ -392,6 +392,9 @@ class OrganizerSettingsSerializer(SettingsSerializer):
|
|||||||
'reusable_media_type_nfc_uid',
|
'reusable_media_type_nfc_uid',
|
||||||
'reusable_media_type_nfc_uid_autocreate_giftcard',
|
'reusable_media_type_nfc_uid_autocreate_giftcard',
|
||||||
'reusable_media_type_nfc_uid_autocreate_giftcard_currency',
|
'reusable_media_type_nfc_uid_autocreate_giftcard_currency',
|
||||||
|
'reusable_media_type_nfc_mf0aes',
|
||||||
|
'reusable_media_type_nfc_mf0aes_autocreate_giftcard',
|
||||||
|
'reusable_media_type_nfc_mf0aes_autocreate_giftcard_currency',
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
|||||||
@@ -19,8 +19,12 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
# 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/>.
|
# <https://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
|
import base64
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from cryptography.hazmat.backends.openssl.backend import Backend
|
||||||
|
from cryptography.hazmat.primitives.asymmetric import padding
|
||||||
|
from cryptography.hazmat.primitives.serialization import load_pem_public_key
|
||||||
from django.db.models import Exists, OuterRef, Q
|
from django.db.models import Exists, OuterRef, Q
|
||||||
from django.db.models.functions import Coalesce
|
from django.db.models.functions import Coalesce
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
@@ -34,6 +38,8 @@ from pretix.api.auth.device import DeviceTokenAuthentication
|
|||||||
from pretix.api.views.version import numeric_version
|
from pretix.api.views.version import numeric_version
|
||||||
from pretix.base.models import CheckinList, Device, SubEvent
|
from pretix.base.models import CheckinList, Device, SubEvent
|
||||||
from pretix.base.models.devices import Gate, generate_api_token
|
from pretix.base.models.devices import Gate, generate_api_token
|
||||||
|
from pretix.base.models.media import MediumKeySet
|
||||||
|
from pretix.base.services.media import get_keysets_for_organizer
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -47,6 +53,17 @@ class InitializationRequestSerializer(serializers.Serializer):
|
|||||||
software_brand = serializers.CharField(max_length=190)
|
software_brand = serializers.CharField(max_length=190)
|
||||||
software_version = serializers.CharField(max_length=190)
|
software_version = serializers.CharField(max_length=190)
|
||||||
info = serializers.JSONField(required=False, allow_null=True)
|
info = serializers.JSONField(required=False, allow_null=True)
|
||||||
|
rsa_pubkey = serializers.CharField(required=False, allow_null=True)
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
if attrs.get('rsa_pubkey'):
|
||||||
|
try:
|
||||||
|
load_pem_public_key(
|
||||||
|
attrs['rsa_pubkey'].encode(), Backend()
|
||||||
|
)
|
||||||
|
except:
|
||||||
|
raise ValidationError({'rsa_pubkey': ['Not a valid public key.']})
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
class UpdateRequestSerializer(serializers.Serializer):
|
class UpdateRequestSerializer(serializers.Serializer):
|
||||||
@@ -57,6 +74,47 @@ class UpdateRequestSerializer(serializers.Serializer):
|
|||||||
software_brand = serializers.CharField(max_length=190)
|
software_brand = serializers.CharField(max_length=190)
|
||||||
software_version = serializers.CharField(max_length=190)
|
software_version = serializers.CharField(max_length=190)
|
||||||
info = serializers.JSONField(required=False, allow_null=True)
|
info = serializers.JSONField(required=False, allow_null=True)
|
||||||
|
rsa_pubkey = serializers.CharField(required=False, allow_null=True)
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
if attrs.get('rsa_pubkey'):
|
||||||
|
try:
|
||||||
|
load_pem_public_key(
|
||||||
|
attrs['rsa_pubkey'].encode(), Backend()
|
||||||
|
)
|
||||||
|
except:
|
||||||
|
raise ValidationError({'rsa_pubkey': ['Not a valid public key.']})
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
|
class RSAEncryptedField(serializers.Field):
|
||||||
|
def to_representation(self, value):
|
||||||
|
public_key = load_pem_public_key(
|
||||||
|
self.context['device'].rsa_pubkey.encode(), Backend()
|
||||||
|
)
|
||||||
|
cipher_text = public_key.encrypt(
|
||||||
|
# RSA/ECB/PKCS1Padding
|
||||||
|
value,
|
||||||
|
padding.PKCS1v15()
|
||||||
|
)
|
||||||
|
return base64.b64encode(cipher_text).decode()
|
||||||
|
|
||||||
|
|
||||||
|
class MediumKeySetSerializer(serializers.ModelSerializer):
|
||||||
|
uid_key = RSAEncryptedField(read_only=True)
|
||||||
|
diversification_key = RSAEncryptedField(read_only=True)
|
||||||
|
organizer = serializers.SlugRelatedField(slug_field='slug', read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = MediumKeySet
|
||||||
|
fields = [
|
||||||
|
'public_id',
|
||||||
|
'organizer',
|
||||||
|
'active',
|
||||||
|
'media_type',
|
||||||
|
'uid_key',
|
||||||
|
'diversification_key',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class GateSerializer(serializers.ModelSerializer):
|
class GateSerializer(serializers.ModelSerializer):
|
||||||
@@ -108,6 +166,7 @@ class InitializeView(APIView):
|
|||||||
device.software_brand = serializer.validated_data.get('software_brand')
|
device.software_brand = serializer.validated_data.get('software_brand')
|
||||||
device.software_version = serializer.validated_data.get('software_version')
|
device.software_version = serializer.validated_data.get('software_version')
|
||||||
device.info = serializer.validated_data.get('info')
|
device.info = serializer.validated_data.get('info')
|
||||||
|
device.rsa_pubkey = serializer.validated_data.get('rsa_pubkey')
|
||||||
device.api_token = generate_api_token()
|
device.api_token = generate_api_token()
|
||||||
device.save()
|
device.save()
|
||||||
|
|
||||||
@@ -130,6 +189,11 @@ class UpdateView(APIView):
|
|||||||
device.os_version = serializer.validated_data.get('os_version')
|
device.os_version = serializer.validated_data.get('os_version')
|
||||||
device.software_brand = serializer.validated_data.get('software_brand')
|
device.software_brand = serializer.validated_data.get('software_brand')
|
||||||
device.software_version = serializer.validated_data.get('software_version')
|
device.software_version = serializer.validated_data.get('software_version')
|
||||||
|
if serializer.validated_data.get('rsa_pubkey') and serializer.validated_data.get('rsa_pubkey') != device.rsa_pubkey:
|
||||||
|
if device.rsa_pubkey:
|
||||||
|
raise ValidationError({'rsa_pubkey': ['You cannot change the rsa_pubkey of the device once it is set.']})
|
||||||
|
else:
|
||||||
|
device.rsa_pubkey = serializer.validated_data.get('rsa_pubkey')
|
||||||
device.info = serializer.validated_data.get('info')
|
device.info = serializer.validated_data.get('info')
|
||||||
device.save()
|
device.save()
|
||||||
device.log_action('pretix.device.updated', data=serializer.validated_data, auth=device)
|
device.log_action('pretix.device.updated', data=serializer.validated_data, auth=device)
|
||||||
@@ -177,8 +241,12 @@ class InfoView(APIView):
|
|||||||
'pretix': __version__,
|
'pretix': __version__,
|
||||||
'pretix_numeric': numeric_version(__version__),
|
'pretix_numeric': numeric_version(__version__),
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
'medium_key_sets': MediumKeySetSerializer(
|
||||||
|
get_keysets_for_organizer(device.organizer),
|
||||||
|
many=True,
|
||||||
|
context={'device': request.auth}
|
||||||
|
).data if device.rsa_pubkey else []
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -104,6 +104,12 @@ class ReusableMediaViewSet(viewsets.ModelViewSet):
|
|||||||
auth=self.request.auth,
|
auth=self.request.auth,
|
||||||
data=merge_dicts(self.request.data, {'id': inst.pk})
|
data=merge_dicts(self.request.data, {'id': inst.pk})
|
||||||
)
|
)
|
||||||
|
mt = MEDIA_TYPES.get(serializer.validated_data["type"])
|
||||||
|
if mt:
|
||||||
|
m = mt.handle_new(self.request.organizer, inst, self.request.user, self.request.auth)
|
||||||
|
if m:
|
||||||
|
s = self.get_serializer(m)
|
||||||
|
return Response({"result": s.data})
|
||||||
|
|
||||||
@transaction.atomic()
|
@transaction.atomic()
|
||||||
def perform_update(self, serializer):
|
def perform_update(self, serializer):
|
||||||
|
|||||||
@@ -945,6 +945,7 @@ with scopes_disabled():
|
|||||||
| Q(addon_to__attendee_email__icontains=value)
|
| Q(addon_to__attendee_email__icontains=value)
|
||||||
| Q(order__code__istartswith=value)
|
| Q(order__code__istartswith=value)
|
||||||
| Q(order__invoice_address__name_cached__icontains=value)
|
| Q(order__invoice_address__name_cached__icontains=value)
|
||||||
|
| Q(order__invoice_address__company__icontains=value)
|
||||||
| Q(order__email__icontains=value)
|
| Q(order__email__icontains=value)
|
||||||
| Q(pk__in=matching_media)
|
| Q(pk__in=matching_media)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ class BaseExporter:
|
|||||||
"""
|
"""
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def render(self, form_data: dict) -> Tuple[str, str, bytes]:
|
def render(self, form_data: dict) -> Tuple[str, str, Optional[bytes]]:
|
||||||
"""
|
"""
|
||||||
Render the exported file and return a tuple consisting of a filename, a file type
|
Render the exported file and return a tuple consisting of a filename, a file type
|
||||||
and file content.
|
and file content.
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ class BaseMediaType:
|
|||||||
def handle_unknown(self, organizer, identifier, user, auth):
|
def handle_unknown(self, organizer, identifier, user, auth):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def handle_new(self, organizer, medium, user, auth):
|
||||||
|
pass
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.verbose_name)
|
return str(self.verbose_name)
|
||||||
|
|
||||||
@@ -108,9 +111,43 @@ class NfcUidMediaType(BaseMediaType):
|
|||||||
return m
|
return m
|
||||||
|
|
||||||
|
|
||||||
|
class NfcMf0aesMediaType(BaseMediaType):
|
||||||
|
identifier = 'nfc_mf0aes'
|
||||||
|
verbose_name = 'NFC Mifare Ultralight AES'
|
||||||
|
medium_created_by_server = False
|
||||||
|
supports_giftcard = True
|
||||||
|
supports_orderposition = False
|
||||||
|
|
||||||
|
def handle_new(self, organizer, medium, user, auth):
|
||||||
|
from pretix.base.models import GiftCard
|
||||||
|
|
||||||
|
if organizer.settings.get(f'reusable_media_type_{self.identifier}_autocreate_giftcard', as_type=bool):
|
||||||
|
with transaction.atomic():
|
||||||
|
gc = GiftCard.objects.create(
|
||||||
|
issuer=organizer,
|
||||||
|
expires=organizer.default_gift_card_expiry,
|
||||||
|
currency=organizer.settings.get(f'reusable_media_type_{self.identifier}_autocreate_giftcard_currency'),
|
||||||
|
)
|
||||||
|
medium.linked_giftcard = gc
|
||||||
|
medium.save()
|
||||||
|
medium.log_action(
|
||||||
|
'pretix.reusable_medium.linked_giftcard.changed',
|
||||||
|
user=user, auth=auth,
|
||||||
|
data={
|
||||||
|
'linked_giftcard': gc.pk
|
||||||
|
}
|
||||||
|
)
|
||||||
|
gc.log_action(
|
||||||
|
'pretix.giftcards.created',
|
||||||
|
user=user, auth=auth,
|
||||||
|
)
|
||||||
|
return medium
|
||||||
|
|
||||||
|
|
||||||
MEDIA_TYPES = {
|
MEDIA_TYPES = {
|
||||||
m.identifier: m for m in [
|
m.identifier: m for m in [
|
||||||
BarcodePlainMediaType(),
|
BarcodePlainMediaType(),
|
||||||
NfcUidMediaType(),
|
NfcUidMediaType(),
|
||||||
|
NfcMf0aesMediaType(),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.http import Http404, HttpRequest, HttpResponse
|
from django.http import Http404, HttpRequest, HttpResponse
|
||||||
from django.middleware.common import CommonMiddleware
|
from django.middleware.common import CommonMiddleware
|
||||||
from django.urls import get_script_prefix
|
from django.urls import get_script_prefix, resolve
|
||||||
from django.utils import timezone, translation
|
from django.utils import timezone, translation
|
||||||
from django.utils.cache import patch_vary_headers
|
from django.utils.cache import patch_vary_headers
|
||||||
from django.utils.deprecation import MiddlewareMixin
|
from django.utils.deprecation import MiddlewareMixin
|
||||||
@@ -230,6 +230,8 @@ class SecurityMiddleware(MiddlewareMixin):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def process_response(self, request, resp):
|
def process_response(self, request, resp):
|
||||||
|
url = resolve(request.path_info)
|
||||||
|
|
||||||
if settings.DEBUG and resp.status_code >= 400:
|
if settings.DEBUG and resp.status_code >= 400:
|
||||||
# Don't use CSP on debug error page as it breaks of Django's fancy error
|
# Don't use CSP on debug error page as it breaks of Django's fancy error
|
||||||
# pages
|
# pages
|
||||||
@@ -249,20 +251,28 @@ class SecurityMiddleware(MiddlewareMixin):
|
|||||||
|
|
||||||
h = {
|
h = {
|
||||||
'default-src': ["{static}"],
|
'default-src': ["{static}"],
|
||||||
'script-src': ['{static}', 'https://checkout.stripe.com', 'https://js.stripe.com', 'https://pay.google.com'],
|
'script-src': ['{static}'],
|
||||||
'object-src': ["'none'"],
|
'object-src': ["'none'"],
|
||||||
'frame-src': ['{static}', 'https://checkout.stripe.com', 'https://js.stripe.com'],
|
'frame-src': ['{static}'],
|
||||||
'style-src': ["{static}", "{media}"],
|
'style-src': ["{static}", "{media}"],
|
||||||
'connect-src': ["{dynamic}", "{media}", "https://checkout.stripe.com"],
|
'connect-src': ["{dynamic}", "{media}"],
|
||||||
'img-src': ["{static}", "{media}", "data:", "https://*.stripe.com"] + img_src,
|
'img-src': ["{static}", "{media}", "data:"] + img_src,
|
||||||
'font-src': ["{static}"],
|
'font-src': ["{static}"],
|
||||||
'media-src': ["{static}", "data:"],
|
'media-src': ["{static}", "data:"],
|
||||||
# form-action is not only used to match on form actions, but also on URLs
|
# 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
|
# form-actions redirect to. In the context of e.g. payment providers or
|
||||||
# single-sign-on this can be nearly anything so we cannot really restrict
|
# single-sign-on this can be nearly anything, so we cannot really restrict
|
||||||
# this. However, we'll restrict it to HTTPS.
|
# this. However, we'll restrict it to HTTPS.
|
||||||
'form-action': ["{dynamic}", "https:"] + (['http:'] if settings.SITE_URL.startswith('http://') else []),
|
'form-action': ["{dynamic}", "https:"] + (['http:'] if settings.SITE_URL.startswith('http://') else []),
|
||||||
}
|
}
|
||||||
|
# Only include pay.google.com for wallet detection purposes on the Payment selection page
|
||||||
|
if (
|
||||||
|
url.url_name == "event.order.pay.change" or
|
||||||
|
(url.url_name == "event.checkout" and url.kwargs['step'] == "payment")
|
||||||
|
):
|
||||||
|
h['script-src'].append('https://pay.google.com')
|
||||||
|
h['frame-src'].append('https://pay.google.com')
|
||||||
|
h['connect-src'].append('https://google.com/pay')
|
||||||
if settings.LOG_CSP:
|
if settings.LOG_CSP:
|
||||||
h['report-uri'] = ["/csp_report/"]
|
h['report-uri'] = ["/csp_report/"]
|
||||||
if 'Content-Security-Policy' in resp:
|
if 'Content-Security-Policy' in resp:
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# Generated by Django 3.2.18 on 2023-05-17 11:32
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('pretixbase', '0243_device_os_name_and_os_version'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='device',
|
||||||
|
name='rsa_pubkey',
|
||||||
|
field=models.TextField(null=True),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='MediumKeySet',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)),
|
||||||
|
('public_id', models.BigIntegerField(unique=True)),
|
||||||
|
('media_type', models.CharField(max_length=100)),
|
||||||
|
('active', models.BooleanField(default=True)),
|
||||||
|
('uid_key', models.BinaryField()),
|
||||||
|
('diversification_key', models.BinaryField()),
|
||||||
|
('organizer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='medium_key_sets', to='pretixbase.organizer')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AddConstraint(
|
||||||
|
model_name='mediumkeyset',
|
||||||
|
constraint=models.UniqueConstraint(condition=models.Q(('active', True)), fields=('organizer', 'media_type'), name='keyset_unique_active'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -97,7 +97,7 @@ def _transactions_mark_order_dirty(order_id, using=None):
|
|||||||
if getattr(dirty_transactions, 'order_ids', None) is None:
|
if getattr(dirty_transactions, 'order_ids', None) is None:
|
||||||
dirty_transactions.order_ids = set()
|
dirty_transactions.order_ids = set()
|
||||||
|
|
||||||
if _check_for_dirty_orders not in [func for savepoint_id, func in conn.run_on_commit]:
|
if _check_for_dirty_orders not in [func for (savepoint_id, func, *__) in conn.run_on_commit]:
|
||||||
transaction.on_commit(_check_for_dirty_orders, using)
|
transaction.on_commit(_check_for_dirty_orders, using)
|
||||||
dirty_transactions.order_ids.clear() # This is necessary to clean up after old threads with rollbacked transactions
|
dirty_transactions.order_ids.clear() # This is necessary to clean up after old threads with rollbacked transactions
|
||||||
|
|
||||||
|
|||||||
@@ -166,6 +166,10 @@ class Device(LoggedModel):
|
|||||||
null=True,
|
null=True,
|
||||||
blank=False
|
blank=False
|
||||||
)
|
)
|
||||||
|
rsa_pubkey = models.TextField(
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
)
|
||||||
info = models.JSONField(
|
info = models.JSONField(
|
||||||
null=True, blank=True,
|
null=True, blank=True,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ from typing import Optional, Tuple
|
|||||||
from zoneinfo import ZoneInfo
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
import dateutil.parser
|
import dateutil.parser
|
||||||
|
import django_redis
|
||||||
from dateutil.tz import datetime_exists
|
from dateutil.tz import datetime_exists
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
@@ -57,7 +58,6 @@ from django.utils.functional import cached_property
|
|||||||
from django.utils.timezone import is_naive, make_aware, now
|
from django.utils.timezone import is_naive, make_aware, now
|
||||||
from django.utils.translation import gettext_lazy as _, pgettext_lazy
|
from django.utils.translation import gettext_lazy as _, pgettext_lazy
|
||||||
from django_countries.fields import Country
|
from django_countries.fields import Country
|
||||||
from django_redis import get_redis_connection
|
|
||||||
from django_scopes import ScopedManager
|
from django_scopes import ScopedManager
|
||||||
from i18nfield.fields import I18nCharField, I18nTextField
|
from i18nfield.fields import I18nCharField, I18nTextField
|
||||||
|
|
||||||
@@ -1910,8 +1910,13 @@ class Quota(LoggedModel):
|
|||||||
|
|
||||||
def rebuild_cache(self, now_dt=None):
|
def rebuild_cache(self, now_dt=None):
|
||||||
if settings.HAS_REDIS:
|
if settings.HAS_REDIS:
|
||||||
rc = get_redis_connection("redis")
|
rc = django_redis.get_redis_connection("redis")
|
||||||
rc.hdel(f'quotas:{self.event_id}:availabilitycache', str(self.pk))
|
p = rc.pipeline()
|
||||||
|
p.hdel(f'quotas:{self.event_id}:availabilitycache', str(self.pk))
|
||||||
|
p.hdel(f'quotas:{self.event_id}:availabilitycache:nocw', str(self.pk))
|
||||||
|
p.hdel(f'quotas:{self.event_id}:availabilitycache:igcl', str(self.pk))
|
||||||
|
p.hdel(f'quotas:{self.event_id}:availabilitycache:nocw:igcl', str(self.pk))
|
||||||
|
p.execute()
|
||||||
self.availability(now_dt=now_dt)
|
self.availability(now_dt=now_dt)
|
||||||
|
|
||||||
def availability(
|
def availability(
|
||||||
|
|||||||
@@ -88,9 +88,7 @@ class LogEntry(models.Model):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('-datetime', '-id')
|
ordering = ('-datetime', '-id')
|
||||||
index_together = [
|
indexes = [models.Index(fields=["datetime", "id"])]
|
||||||
['datetime', 'id']
|
|
||||||
]
|
|
||||||
|
|
||||||
def display(self):
|
def display(self):
|
||||||
from ..signals import logentry_display
|
from ..signals import logentry_display
|
||||||
|
|||||||
@@ -121,5 +121,30 @@ class ReusableMedium(LoggedModel):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = (("identifier", "type", "organizer"),)
|
unique_together = (("identifier", "type", "organizer"),)
|
||||||
index_together = (("identifier", "type", "organizer"), ("updated", "id"))
|
indexes = [
|
||||||
|
models.Index(fields=("identifier", "type", "organizer")),
|
||||||
|
models.Index(fields=("updated", "id")),
|
||||||
|
]
|
||||||
ordering = "identifier", "type", "organizer"
|
ordering = "identifier", "type", "organizer"
|
||||||
|
|
||||||
|
|
||||||
|
class MediumKeySet(models.Model):
|
||||||
|
organizer = models.ForeignKey('Organizer', on_delete=models.CASCADE, related_name='medium_key_sets')
|
||||||
|
public_id = models.BigIntegerField(
|
||||||
|
unique=True,
|
||||||
|
)
|
||||||
|
media_type = models.CharField(max_length=100)
|
||||||
|
active = models.BooleanField(default=True)
|
||||||
|
uid_key = models.BinaryField()
|
||||||
|
diversification_key = models.BinaryField()
|
||||||
|
|
||||||
|
objects = ScopedManager(organizer='organizer')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
constraints = [
|
||||||
|
models.UniqueConstraint(
|
||||||
|
fields=["organizer", "media_type"],
|
||||||
|
condition=Q(active=True),
|
||||||
|
name="keyset_unique_active",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|||||||
@@ -270,9 +270,9 @@ class Order(LockModel, LoggedModel):
|
|||||||
verbose_name = _("Order")
|
verbose_name = _("Order")
|
||||||
verbose_name_plural = _("Orders")
|
verbose_name_plural = _("Orders")
|
||||||
ordering = ("-datetime", "-pk")
|
ordering = ("-datetime", "-pk")
|
||||||
index_together = [
|
indexes = [
|
||||||
["datetime", "id"],
|
models.Index(fields=["datetime", "id"]),
|
||||||
["last_modified", "id"],
|
models.Index(fields=["last_modified", "id"]),
|
||||||
]
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@@ -907,6 +907,11 @@ class Order(LockModel, LoggedModel):
|
|||||||
return self.expires
|
return self.expires
|
||||||
|
|
||||||
expires = self.expires.date() + timedelta(days=delay)
|
expires = self.expires.date() + timedelta(days=delay)
|
||||||
|
if self.event.settings.get('payment_term_weekdays'):
|
||||||
|
if expires.weekday() == 5:
|
||||||
|
expires += timedelta(days=2)
|
||||||
|
elif expires.weekday() == 6:
|
||||||
|
expires += timedelta(days=1)
|
||||||
|
|
||||||
tz = ZoneInfo(self.event.settings.timezone)
|
tz = ZoneInfo(self.event.settings.timezone)
|
||||||
expires = make_aware(datetime.combine(
|
expires = make_aware(datetime.combine(
|
||||||
@@ -1667,12 +1672,13 @@ class OrderPayment(models.Model):
|
|||||||
if status_change:
|
if status_change:
|
||||||
self.order.create_transactions()
|
self.order.create_transactions()
|
||||||
|
|
||||||
def fail(self, info=None, user=None, auth=None, log_data=None):
|
def fail(self, info=None, user=None, auth=None, log_data=None, send_mail=True):
|
||||||
"""
|
"""
|
||||||
Marks the order as failed and sets info to ``info``, but only if the order is in ``created`` or ``pending``
|
Marks the order as failed and sets info to ``info``, but only if the order is in ``created`` or ``pending``
|
||||||
state. This is equivalent to setting ``state`` to ``OrderPayment.PAYMENT_STATE_FAILED`` and logging a failure,
|
state. This is equivalent to setting ``state`` to ``OrderPayment.PAYMENT_STATE_FAILED`` and logging a failure,
|
||||||
but it adds strong database logging since we do not want to report a failure for an order that has just
|
but it adds strong database locking since we do not want to report a failure for an order that has just
|
||||||
been marked as paid.
|
been marked as paid.
|
||||||
|
:param send_mail: Whether an email should be sent to the user about this event (default: ``True``).
|
||||||
"""
|
"""
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
locked_instance = OrderPayment.objects.select_for_update(of=OF_SELF).get(pk=self.pk)
|
locked_instance = OrderPayment.objects.select_for_update(of=OF_SELF).get(pk=self.pk)
|
||||||
@@ -1697,6 +1703,17 @@ class OrderPayment(models.Model):
|
|||||||
'info': info,
|
'info': info,
|
||||||
'data': log_data,
|
'data': log_data,
|
||||||
}, user=user, auth=auth)
|
}, user=user, auth=auth)
|
||||||
|
|
||||||
|
if send_mail:
|
||||||
|
with language(self.order.locale, self.order.event.settings.region):
|
||||||
|
email_subject = self.order.event.settings.mail_subject_order_payment_failed
|
||||||
|
email_template = self.order.event.settings.mail_text_order_payment_failed
|
||||||
|
email_context = get_email_context(event=self.order.event, order=self.order)
|
||||||
|
self.order.send_mail(
|
||||||
|
email_subject, email_template, email_context,
|
||||||
|
'pretix.event.order.email.payment_failed', user=user, auth=auth,
|
||||||
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def confirm(self, count_waitinglist=True, send_mail=True, force=False, user=None, auth=None, mail_text='',
|
def confirm(self, count_waitinglist=True, send_mail=True, force=False, user=None, auth=None, mail_text='',
|
||||||
@@ -2739,8 +2756,8 @@ class Transaction(models.Model):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = 'datetime', 'pk'
|
ordering = 'datetime', 'pk'
|
||||||
index_together = [
|
indexes = [
|
||||||
['datetime', 'id']
|
models.Index(fields=['datetime', 'id'])
|
||||||
]
|
]
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
|||||||
@@ -805,7 +805,7 @@ class QuestionColumn(ImportColumn):
|
|||||||
return self.q.clean_answer(value)
|
return self.q.clean_answer(value)
|
||||||
|
|
||||||
def assign(self, value, order, position, invoice_address, **kwargs):
|
def assign(self, value, order, position, invoice_address, **kwargs):
|
||||||
if value:
|
if value is not None:
|
||||||
if not hasattr(order, '_answers'):
|
if not hasattr(order, '_answers'):
|
||||||
order._answers = []
|
order._answers = []
|
||||||
if isinstance(value, QuestionOption):
|
if isinstance(value, QuestionOption):
|
||||||
|
|||||||
+98
-14
@@ -43,16 +43,18 @@ import subprocess
|
|||||||
import tempfile
|
import tempfile
|
||||||
import unicodedata
|
import unicodedata
|
||||||
import uuid
|
import uuid
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict, defaultdict
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import jsonschema
|
import jsonschema
|
||||||
|
import reportlab.rl_config
|
||||||
from bidi.algorithm import get_display
|
from bidi.algorithm import get_display
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.staticfiles import finders
|
from django.contrib.staticfiles import finders
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.db.models import Max, Min
|
from django.db.models import Max, Min
|
||||||
|
from django.db.models.fields.files import FieldFile
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.utils.deconstruct import deconstructible
|
from django.utils.deconstruct import deconstructible
|
||||||
from django.utils.formats import date_format
|
from django.utils.formats import date_format
|
||||||
@@ -60,7 +62,8 @@ from django.utils.html import conditional_escape
|
|||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.utils.translation import gettext_lazy as _, pgettext
|
from django.utils.translation import gettext_lazy as _, pgettext
|
||||||
from i18nfield.strings import LazyI18nString
|
from i18nfield.strings import LazyI18nString
|
||||||
from pypdf import PdfReader
|
from pypdf import PdfReader, PdfWriter, Transformation
|
||||||
|
from pypdf.generic import RectangleObject
|
||||||
from reportlab.graphics import renderPDF
|
from reportlab.graphics import renderPDF
|
||||||
from reportlab.graphics.barcode.qr import QrCodeWidget
|
from reportlab.graphics.barcode.qr import QrCodeWidget
|
||||||
from reportlab.graphics.shapes import Drawing
|
from reportlab.graphics.shapes import Drawing
|
||||||
@@ -85,6 +88,9 @@ from pretix.presale.style import get_fonts
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
if not settings.DEBUG:
|
||||||
|
reportlab.rl_config.shapeChecking = 0
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_VARIABLES = OrderedDict((
|
DEFAULT_VARIABLES = OrderedDict((
|
||||||
("secret", {
|
("secret", {
|
||||||
@@ -102,7 +108,10 @@ DEFAULT_VARIABLES = OrderedDict((
|
|||||||
("positionid", {
|
("positionid", {
|
||||||
"label": _("Order position number"),
|
"label": _("Order position number"),
|
||||||
"editor_sample": "1",
|
"editor_sample": "1",
|
||||||
"evaluate": lambda orderposition, order, event: str(orderposition.positionid)
|
"evaluate": lambda orderposition, order, event: str(orderposition.positionid),
|
||||||
|
# There is no performance gain in using evaluate_bulk here, but we want to make sure it is used somewhere
|
||||||
|
# in core to make sure we notice if the implementation of the API breaks.
|
||||||
|
"evaluate_bulk": lambda orderpositions: [str(p.positionid) for p in orderpositions],
|
||||||
}),
|
}),
|
||||||
("order_positionid", {
|
("order_positionid", {
|
||||||
"label": _("Order code and position number"),
|
"label": _("Order code and position number"),
|
||||||
@@ -355,14 +364,9 @@ DEFAULT_VARIABLES = OrderedDict((
|
|||||||
}),
|
}),
|
||||||
("addons", {
|
("addons", {
|
||||||
"label": _("List of Add-Ons"),
|
"label": _("List of Add-Ons"),
|
||||||
"editor_sample": _("Add-on 1\nAdd-on 2"),
|
"editor_sample": _("Add-on 1\n2x Add-on 2"),
|
||||||
"evaluate": lambda op, order, ev: "\n".join([
|
"evaluate": lambda op, order, ev: "\n".join([
|
||||||
'{} - {}'.format(p.item.name, p.variation.value) if p.variation else str(p.item.name)
|
str(p) for p in generate_compressed_addon_list(op, order, ev)
|
||||||
for p in (
|
|
||||||
op.addons.all() if 'addons' in getattr(op, '_prefetched_objects_cache', {})
|
|
||||||
else op.addons.select_related('item', 'variation')
|
|
||||||
)
|
|
||||||
if not p.canceled
|
|
||||||
])
|
])
|
||||||
}),
|
}),
|
||||||
("organizer", {
|
("organizer", {
|
||||||
@@ -696,6 +700,30 @@ def get_seat(op: OrderPosition):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def generate_compressed_addon_list(op, order, event):
|
||||||
|
itemcount = defaultdict(int)
|
||||||
|
addons = (
|
||||||
|
op.addons.all() if 'addons' in getattr(op, '_prefetched_objects_cache', {})
|
||||||
|
else op.addons.select_related('item', 'variation')
|
||||||
|
)
|
||||||
|
for pos in addons:
|
||||||
|
itemcount[pos.item, pos.variation] += 1
|
||||||
|
|
||||||
|
addonlist = []
|
||||||
|
for (item, variation), count in itemcount.items():
|
||||||
|
if variation:
|
||||||
|
if count > 1:
|
||||||
|
addonlist.append('{}x {} - {}'.format(count, item.name, variation.value))
|
||||||
|
else:
|
||||||
|
addonlist.append('{} - {}'.format(item.name, variation.value))
|
||||||
|
else:
|
||||||
|
if count > 1:
|
||||||
|
addonlist.append('{}x {}'.format(count, item.name))
|
||||||
|
else:
|
||||||
|
addonlist.append(item.name)
|
||||||
|
return addonlist
|
||||||
|
|
||||||
|
|
||||||
class Renderer:
|
class Renderer:
|
||||||
|
|
||||||
def __init__(self, event, layout, background_file):
|
def __init__(self, event, layout, background_file):
|
||||||
@@ -873,6 +901,11 @@ class Renderer:
|
|||||||
anchor='c', # centered in frame
|
anchor='c', # centered in frame
|
||||||
mask='auto'
|
mask='auto'
|
||||||
)
|
)
|
||||||
|
if isinstance(image_file, FieldFile):
|
||||||
|
# ThumbnailingImageReader "closes" the file, so it's no use to use the same file pointer
|
||||||
|
# in case we need it again. For FieldFile, fortunately, there is an easy way to make the file
|
||||||
|
# refresh itself when it is used next.
|
||||||
|
del image_file.file
|
||||||
except:
|
except:
|
||||||
logger.exception("Can not load or resize image")
|
logger.exception("Can not load or resize image")
|
||||||
canvas.saveState()
|
canvas.saveState()
|
||||||
@@ -992,7 +1025,10 @@ class Renderer:
|
|||||||
elif o['type'] == "poweredby":
|
elif o['type'] == "poweredby":
|
||||||
self._draw_poweredby(canvas, op, o)
|
self._draw_poweredby(canvas, op, o)
|
||||||
if self.bg_pdf:
|
if self.bg_pdf:
|
||||||
page_size = (self.bg_pdf.pages[0].mediabox[2], self.bg_pdf.pages[0].mediabox[3])
|
page_size = (
|
||||||
|
self.bg_pdf.pages[0].mediabox[2] - self.bg_pdf.pages[0].mediabox[0],
|
||||||
|
self.bg_pdf.pages[0].mediabox[3] - self.bg_pdf.pages[0].mediabox[1]
|
||||||
|
)
|
||||||
if self.bg_pdf.pages[0].get('/Rotate') in (90, 270):
|
if self.bg_pdf.pages[0].get('/Rotate') in (90, 270):
|
||||||
# swap dimensions due to pdf being rotated
|
# swap dimensions due to pdf being rotated
|
||||||
page_size = page_size[::-1]
|
page_size = page_size[::-1]
|
||||||
@@ -1020,14 +1056,12 @@ class Renderer:
|
|||||||
with open(os.path.join(d, 'out.pdf'), 'rb') as f:
|
with open(os.path.join(d, 'out.pdf'), 'rb') as f:
|
||||||
return BytesIO(f.read())
|
return BytesIO(f.read())
|
||||||
else:
|
else:
|
||||||
from pypdf import PdfReader, PdfWriter, Transformation
|
|
||||||
from pypdf.generic import RectangleObject
|
|
||||||
buffer.seek(0)
|
buffer.seek(0)
|
||||||
new_pdf = PdfReader(buffer)
|
new_pdf = PdfReader(buffer)
|
||||||
output = PdfWriter()
|
output = PdfWriter()
|
||||||
|
|
||||||
for i, page in enumerate(new_pdf.pages):
|
for i, page in enumerate(new_pdf.pages):
|
||||||
bg_page = copy.copy(self.bg_pdf.pages[i])
|
bg_page = copy.deepcopy(self.bg_pdf.pages[i])
|
||||||
bg_rotation = bg_page.get('/Rotate')
|
bg_rotation = bg_page.get('/Rotate')
|
||||||
if bg_rotation:
|
if bg_rotation:
|
||||||
# /Rotate is clockwise, transformation.rotate is counter-clockwise
|
# /Rotate is clockwise, transformation.rotate is counter-clockwise
|
||||||
@@ -1064,6 +1098,56 @@ class Renderer:
|
|||||||
return outbuffer
|
return outbuffer
|
||||||
|
|
||||||
|
|
||||||
|
def merge_background(fg_pdf, bg_pdf, out_file, compress):
|
||||||
|
if settings.PDFTK:
|
||||||
|
with tempfile.TemporaryDirectory() as d:
|
||||||
|
fg_filename = os.path.join(d, 'fg.pdf')
|
||||||
|
bg_filename = os.path.join(d, 'bg.pdf')
|
||||||
|
fg_pdf.write(fg_filename)
|
||||||
|
bg_pdf.write(bg_filename)
|
||||||
|
pdftk_cmd = [
|
||||||
|
settings.PDFTK,
|
||||||
|
fg_filename,
|
||||||
|
'multibackground',
|
||||||
|
bg_filename,
|
||||||
|
'output',
|
||||||
|
'-',
|
||||||
|
]
|
||||||
|
if compress:
|
||||||
|
pdftk_cmd.append('compress')
|
||||||
|
subprocess.run(pdftk_cmd, check=True, stdout=out_file)
|
||||||
|
else:
|
||||||
|
output = PdfWriter()
|
||||||
|
for i, page in enumerate(fg_pdf.pages):
|
||||||
|
bg_page = copy.deepcopy(bg_pdf.pages[i])
|
||||||
|
bg_rotation = bg_page.get('/Rotate')
|
||||||
|
if bg_rotation:
|
||||||
|
# /Rotate is clockwise, transformation.rotate is counter-clockwise
|
||||||
|
t = Transformation().rotate(bg_rotation)
|
||||||
|
w = float(page.mediabox.getWidth())
|
||||||
|
h = float(page.mediabox.getHeight())
|
||||||
|
if bg_rotation in (90, 270):
|
||||||
|
# offset due to rotation base
|
||||||
|
if bg_rotation == 90:
|
||||||
|
t = t.translate(h, 0)
|
||||||
|
else:
|
||||||
|
t = t.translate(0, w)
|
||||||
|
# rotate mediabox as well
|
||||||
|
page.mediabox = RectangleObject((
|
||||||
|
page.mediabox.left.as_numeric(),
|
||||||
|
page.mediabox.bottom.as_numeric(),
|
||||||
|
page.mediabox.top.as_numeric(),
|
||||||
|
page.mediabox.right.as_numeric(),
|
||||||
|
))
|
||||||
|
page.trimbox = page.mediabox
|
||||||
|
elif bg_rotation == 180:
|
||||||
|
t = t.translate(w, h)
|
||||||
|
page.add_transformation(t)
|
||||||
|
bg_page.merge_page(page)
|
||||||
|
output.add_page(bg_page)
|
||||||
|
output.write(out_file)
|
||||||
|
|
||||||
|
|
||||||
@deconstructible
|
@deconstructible
|
||||||
class PdfLayoutValidator:
|
class PdfLayoutValidator:
|
||||||
def __call__(self, value):
|
def __call__(self, value):
|
||||||
|
|||||||
@@ -141,9 +141,10 @@ error_messages = {
|
|||||||
'price_not_a_number': gettext_lazy('The entered price is not a number.'),
|
'price_not_a_number': gettext_lazy('The entered price is not a number.'),
|
||||||
'price_too_high': gettext_lazy('The entered price is to high.'),
|
'price_too_high': gettext_lazy('The entered price is to high.'),
|
||||||
'voucher_invalid': gettext_lazy('This voucher code is not known in our database.'),
|
'voucher_invalid': gettext_lazy('This voucher code is not known in our database.'),
|
||||||
'voucher_min_usages': gettext_lazy(
|
'voucher_min_usages': ngettext_lazy(
|
||||||
'The voucher code "%(voucher)s" can only be used if you select at least %(number)s '
|
'The voucher code "%(voucher)s" can only be used if you select at least %(number)s matching products.',
|
||||||
'matching products.'
|
'The voucher code "%(voucher)s" can only be used if you select at least %(number)s matching products.',
|
||||||
|
'number'
|
||||||
),
|
),
|
||||||
'voucher_min_usages_removed': ngettext_lazy(
|
'voucher_min_usages_removed': ngettext_lazy(
|
||||||
'The voucher code "%(voucher)s" can only be used if you select at least %(number)s matching products. '
|
'The voucher code "%(voucher)s" can only be used if you select at least %(number)s matching products. '
|
||||||
|
|||||||
@@ -86,8 +86,8 @@ def _build_time(t=None, value=None, ev=None, now_dt=None):
|
|||||||
return ev.date_admission or ev.date_from
|
return ev.date_admission or ev.date_from
|
||||||
|
|
||||||
|
|
||||||
def _logic_annotate_for_graphic_explain(rules, ev, rule_data):
|
def _logic_annotate_for_graphic_explain(rules, ev, rule_data, now_dt):
|
||||||
logic_environment = _get_logic_environment(ev)
|
logic_environment = _get_logic_environment(ev, now_dt)
|
||||||
event = ev if isinstance(ev, Event) else ev.event
|
event = ev if isinstance(ev, Event) else ev.event
|
||||||
|
|
||||||
def _evaluate_inners(r):
|
def _evaluate_inners(r):
|
||||||
@@ -152,7 +152,7 @@ def _logic_explain(rules, ev, rule_data, now_dt=None):
|
|||||||
get in before 17:00". In the middle of the night it would switch to "You can only get in after 09:00".
|
get in before 17:00". In the middle of the night it would switch to "You can only get in after 09:00".
|
||||||
"""
|
"""
|
||||||
now_dt = now_dt or now()
|
now_dt = now_dt or now()
|
||||||
logic_environment = _get_logic_environment(ev)
|
logic_environment = _get_logic_environment(ev, now_dt)
|
||||||
_var_values = {'False': False, 'True': True}
|
_var_values = {'False': False, 'True': True}
|
||||||
_var_explanations = {}
|
_var_explanations = {}
|
||||||
|
|
||||||
@@ -229,7 +229,7 @@ def _logic_explain(rules, ev, rule_data, now_dt=None):
|
|||||||
for vname, data in _var_explanations.items():
|
for vname, data in _var_explanations.items():
|
||||||
var, operator, rhs = data['var'], data['operator'], data['rhs']
|
var, operator, rhs = data['var'], data['operator'], data['rhs']
|
||||||
if var == 'now':
|
if var == 'now':
|
||||||
compare_to = _build_time(*rhs[0]['buildTime'], ev=ev).astimezone(ev.timezone)
|
compare_to = _build_time(*rhs[0]['buildTime'], ev=ev, now_dt=now_dt).astimezone(ev.timezone)
|
||||||
tolerance = timedelta(minutes=float(rhs[1])) if len(rhs) > 1 and rhs[1] else timedelta(seconds=0)
|
tolerance = timedelta(minutes=float(rhs[1])) if len(rhs) > 1 and rhs[1] else timedelta(seconds=0)
|
||||||
if operator == 'isBefore':
|
if operator == 'isBefore':
|
||||||
compare_to += tolerance
|
compare_to += tolerance
|
||||||
@@ -337,7 +337,7 @@ def _logic_explain(rules, ev, rule_data, now_dt=None):
|
|||||||
return ', '.join(var_texts[v] for v in paths_with_min_weight[0] if not _var_values[v])
|
return ', '.join(var_texts[v] for v in paths_with_min_weight[0] if not _var_values[v])
|
||||||
|
|
||||||
|
|
||||||
def _get_logic_environment(ev):
|
def _get_logic_environment(ev, now_dt):
|
||||||
# Every change to our supported JSON logic must be done
|
# Every change to our supported JSON logic must be done
|
||||||
# * in pretix.base.services.checkin
|
# * in pretix.base.services.checkin
|
||||||
# * in pretix.base.models.checkin
|
# * in pretix.base.models.checkin
|
||||||
@@ -354,7 +354,7 @@ def _get_logic_environment(ev):
|
|||||||
logic.add_operation('objectList', lambda *objs: list(objs))
|
logic.add_operation('objectList', lambda *objs: list(objs))
|
||||||
logic.add_operation('lookup', lambda model, pk, str: int(pk))
|
logic.add_operation('lookup', lambda model, pk, str: int(pk))
|
||||||
logic.add_operation('inList', lambda a, b: a in b)
|
logic.add_operation('inList', lambda a, b: a in b)
|
||||||
logic.add_operation('buildTime', partial(_build_time, ev=ev))
|
logic.add_operation('buildTime', partial(_build_time, ev=ev, now_dt=now_dt))
|
||||||
logic.add_operation('isBefore', is_before)
|
logic.add_operation('isBefore', is_before)
|
||||||
logic.add_operation('isAfter', lambda t1, t2, tol=None: is_before(t2, t1, tol))
|
logic.add_operation('isAfter', lambda t1, t2, tol=None: is_before(t2, t1, tol))
|
||||||
return logic
|
return logic
|
||||||
@@ -861,7 +861,7 @@ def perform_checkin(op: OrderPosition, clist: CheckinList, given_answers: dict,
|
|||||||
|
|
||||||
if type == Checkin.TYPE_ENTRY and clist.rules:
|
if type == Checkin.TYPE_ENTRY and clist.rules:
|
||||||
rule_data = LazyRuleVars(op, clist, dt)
|
rule_data = LazyRuleVars(op, clist, dt)
|
||||||
logic = _get_logic_environment(op.subevent or clist.event)
|
logic = _get_logic_environment(op.subevent or clist.event, now_dt=dt)
|
||||||
if not logic.apply(clist.rules, rule_data):
|
if not logic.apply(clist.rules, rule_data):
|
||||||
if force:
|
if force:
|
||||||
force_used = True
|
force_used = True
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ from typing import Any, Dict, Union
|
|||||||
from celery.exceptions import MaxRetriesExceededError
|
from celery.exceptions import MaxRetriesExceededError
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.files.base import ContentFile
|
from django.core.files.base import ContentFile
|
||||||
from django.db import connection, transaction
|
from django.db import close_old_connections, connection, transaction
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.utils.timezone import now, override
|
from django.utils.timezone import now, override
|
||||||
from django.utils.translation import gettext
|
from django.utils.translation import gettext
|
||||||
@@ -86,9 +86,12 @@ def export(self, event: Event, fileid: str, provider: str, form_data: Dict[str,
|
|||||||
gettext('Your export did not contain any data.')
|
gettext('Your export did not contain any data.')
|
||||||
)
|
)
|
||||||
file.filename, file.type, data = d
|
file.filename, file.type, data = d
|
||||||
|
|
||||||
|
close_old_connections() # This task can run very long, we might need a new DB connection
|
||||||
|
|
||||||
f = ContentFile(data)
|
f = ContentFile(data)
|
||||||
file.file.save(cachedfile_name(file, file.filename), f)
|
file.file.save(cachedfile_name(file, file.filename), f)
|
||||||
return file.pk
|
return str(file.pk)
|
||||||
|
|
||||||
|
|
||||||
@app.task(base=ProfiledOrganizerUserTask, throws=(ExportError,), bind=True)
|
@app.task(base=ProfiledOrganizerUserTask, throws=(ExportError,), bind=True)
|
||||||
@@ -154,9 +157,12 @@ def multiexport(self, organizer: Organizer, user: User, device: int, token: int,
|
|||||||
gettext('Your export did not contain any data.')
|
gettext('Your export did not contain any data.')
|
||||||
)
|
)
|
||||||
file.filename, file.type, data = d
|
file.filename, file.type, data = d
|
||||||
|
|
||||||
|
close_old_connections() # This task can run very long, we might need a new DB connection
|
||||||
|
|
||||||
f = ContentFile(data)
|
f = ContentFile(data)
|
||||||
file.file.save(cachedfile_name(file, file.filename), f)
|
file.file.save(cachedfile_name(file, file.filename), f)
|
||||||
return file.pk
|
return str(file.pk)
|
||||||
|
|
||||||
|
|
||||||
def _run_scheduled_export(schedule, context: Union[Event, Organizer], exporter, config_url, retry_func, has_permission):
|
def _run_scheduled_export(schedule, context: Union[Event, Organizer], exporter, config_url, retry_func, has_permission):
|
||||||
@@ -214,6 +220,11 @@ def _run_scheduled_export(schedule, context: Union[Event, Organizer], exporter,
|
|||||||
raise ExportError(
|
raise ExportError(
|
||||||
gettext('Your exported data exceeded the size limit for scheduled exports.')
|
gettext('Your exported data exceeded the size limit for scheduled exports.')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
conn = transaction.get_connection()
|
||||||
|
if not conn.in_atomic_block: # atomic execution only happens during tests or with celery always_eager on
|
||||||
|
close_old_connections() # This task can run very long, we might need a new DB connection
|
||||||
|
|
||||||
f = ContentFile(data)
|
f = ContentFile(data)
|
||||||
file.file.save(cachedfile_name(file, file.filename), f)
|
file.file.save(cachedfile_name(file, file.filename), f)
|
||||||
except ExportEmptyError as e:
|
except ExportEmptyError as e:
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
#
|
||||||
|
# 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 secrets
|
||||||
|
|
||||||
|
from django.db import IntegrityError
|
||||||
|
from django.db.models import Q
|
||||||
|
from django_scopes import scopes_disabled
|
||||||
|
|
||||||
|
from pretix.base.models import GiftCardAcceptance
|
||||||
|
from pretix.base.models.media import MediumKeySet
|
||||||
|
|
||||||
|
|
||||||
|
def create_nfc_mf0aes_keyset(organizer):
|
||||||
|
for i in range(20):
|
||||||
|
public_id = secrets.randbelow(2 ** 32)
|
||||||
|
uid_key = secrets.token_bytes(16)
|
||||||
|
diversification_key = secrets.token_bytes(16)
|
||||||
|
try:
|
||||||
|
return MediumKeySet.objects.create(
|
||||||
|
organizer=organizer,
|
||||||
|
media_type="nfc_mf0aes",
|
||||||
|
public_id=public_id,
|
||||||
|
diversification_key=diversification_key,
|
||||||
|
uid_key=uid_key,
|
||||||
|
active=True,
|
||||||
|
)
|
||||||
|
except IntegrityError: # either race condition with another thread or duplicate public ID
|
||||||
|
try:
|
||||||
|
return MediumKeySet.objects.get(
|
||||||
|
organizer=organizer,
|
||||||
|
media_type="nfc_mf0aes",
|
||||||
|
active=True,
|
||||||
|
)
|
||||||
|
except MediumKeySet.DoesNotExist:
|
||||||
|
continue # duplicate public ID, let's try again
|
||||||
|
|
||||||
|
|
||||||
|
@scopes_disabled()
|
||||||
|
def get_keysets_for_organizer(organizer):
|
||||||
|
sets = list(MediumKeySet.objects.filter(
|
||||||
|
Q(organizer=organizer) | Q(organizer__in=GiftCardAcceptance.objects.filter(
|
||||||
|
acceptor=organizer,
|
||||||
|
active=True,
|
||||||
|
reusable_media=True,
|
||||||
|
).values_list("issuer_id", flat=True))
|
||||||
|
))
|
||||||
|
if organizer.settings.reusable_media_type_nfc_mf0aes and not any(
|
||||||
|
ks.organizer == organizer and ks.media_type == "nfc_mf0aes" for ks in sets
|
||||||
|
):
|
||||||
|
new_set = create_nfc_mf0aes_keyset(organizer)
|
||||||
|
if new_set:
|
||||||
|
sets.append(new_set)
|
||||||
|
return sets
|
||||||
@@ -2476,6 +2476,11 @@ class OrderChangeManager:
|
|||||||
split_order.status = Order.STATUS_PAID
|
split_order.status = Order.STATUS_PAID
|
||||||
else:
|
else:
|
||||||
split_order.status = Order.STATUS_PENDING
|
split_order.status = Order.STATUS_PENDING
|
||||||
|
if self.order.status == Order.STATUS_PAID:
|
||||||
|
split_order.set_expires(
|
||||||
|
now(),
|
||||||
|
list(set(p.subevent_id for p in split_positions))
|
||||||
|
)
|
||||||
split_order.save()
|
split_order.save()
|
||||||
|
|
||||||
if offset_amount > Decimal('0.00'):
|
if offset_amount > Decimal('0.00'):
|
||||||
|
|||||||
@@ -24,13 +24,13 @@ import time
|
|||||||
from collections import Counter, defaultdict
|
from collections import Counter, defaultdict
|
||||||
from itertools import zip_longest
|
from itertools import zip_longest
|
||||||
|
|
||||||
|
import django_redis
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import (
|
from django.db.models import (
|
||||||
Case, Count, F, Func, Max, OuterRef, Q, Subquery, Sum, Value, When,
|
Case, Count, F, Func, Max, OuterRef, Q, Subquery, Sum, Value, When,
|
||||||
)
|
)
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django_redis import get_redis_connection
|
|
||||||
|
|
||||||
from pretix.base.models import (
|
from pretix.base.models import (
|
||||||
CartPosition, Checkin, Order, OrderPosition, Quota, Voucher,
|
CartPosition, Checkin, Order, OrderPosition, Quota, Voucher,
|
||||||
@@ -102,6 +102,12 @@ class QuotaAvailability:
|
|||||||
self.count_waitinglist = defaultdict(int)
|
self.count_waitinglist = defaultdict(int)
|
||||||
self.count_cart = defaultdict(int)
|
self.count_cart = defaultdict(int)
|
||||||
|
|
||||||
|
self._cache_key_suffix = ""
|
||||||
|
if not self._count_waitinglist:
|
||||||
|
self._cache_key_suffix += ":nocw"
|
||||||
|
if self._ignore_closed:
|
||||||
|
self._cache_key_suffix += ":igcl"
|
||||||
|
|
||||||
self.sizes = {}
|
self.sizes = {}
|
||||||
|
|
||||||
def queue(self, *quota):
|
def queue(self, *quota):
|
||||||
@@ -121,17 +127,14 @@ class QuotaAvailability:
|
|||||||
if self._full_results:
|
if self._full_results:
|
||||||
raise ValueError("You cannot combine full_results and allow_cache.")
|
raise ValueError("You cannot combine full_results and allow_cache.")
|
||||||
|
|
||||||
elif not self._count_waitinglist:
|
|
||||||
raise ValueError("If you set allow_cache, you need to set count_waitinglist.")
|
|
||||||
|
|
||||||
elif settings.HAS_REDIS:
|
elif settings.HAS_REDIS:
|
||||||
rc = get_redis_connection("redis")
|
rc = django_redis.get_redis_connection("redis")
|
||||||
quotas_by_event = defaultdict(list)
|
quotas_by_event = defaultdict(list)
|
||||||
for q in [_q for _q in self._queue if _q.id in quota_ids_set]:
|
for q in [_q for _q in self._queue if _q.id in quota_ids_set]:
|
||||||
quotas_by_event[q.event_id].append(q)
|
quotas_by_event[q.event_id].append(q)
|
||||||
|
|
||||||
for eventid, evquotas in quotas_by_event.items():
|
for eventid, evquotas in quotas_by_event.items():
|
||||||
d = rc.hmget(f'quotas:{eventid}:availabilitycache', [str(q.pk) for q in evquotas])
|
d = rc.hmget(f'quotas:{eventid}:availabilitycache{self._cache_key_suffix}', [str(q.pk) for q in evquotas])
|
||||||
for redisval, q in zip(d, evquotas):
|
for redisval, q in zip(d, evquotas):
|
||||||
if redisval is not None:
|
if redisval is not None:
|
||||||
data = [rv for rv in redisval.decode().split(',')]
|
data = [rv for rv in redisval.decode().split(',')]
|
||||||
@@ -164,12 +167,12 @@ class QuotaAvailability:
|
|||||||
if not settings.HAS_REDIS or not quotas:
|
if not settings.HAS_REDIS or not quotas:
|
||||||
return
|
return
|
||||||
|
|
||||||
rc = get_redis_connection("redis")
|
rc = django_redis.get_redis_connection("redis")
|
||||||
# We write the computed availability to redis in a per-event hash as
|
# We write the computed availability to redis in a per-event hash as
|
||||||
#
|
#
|
||||||
# quota_id -> (availability_state, availability_number, timestamp).
|
# quota_id -> (availability_state, availability_number, timestamp).
|
||||||
#
|
#
|
||||||
# We store this in a hash instead of inidividual values to avoid making two many redis requests
|
# We store this in a hash instead of individual values to avoid making too many redis requests
|
||||||
# which would introduce latency.
|
# which would introduce latency.
|
||||||
|
|
||||||
# The individual entries in the hash are "valid" for 120 seconds. This means in a typical peak scenario with
|
# The individual entries in the hash are "valid" for 120 seconds. This means in a typical peak scenario with
|
||||||
@@ -179,16 +182,16 @@ class QuotaAvailability:
|
|||||||
# these quotas. We choose 10 seconds since that should be well above the duration of a write.
|
# these quotas. We choose 10 seconds since that should be well above the duration of a write.
|
||||||
|
|
||||||
lock_name = '_'.join([str(p) for p in sorted([q.pk for q in quotas])])
|
lock_name = '_'.join([str(p) for p in sorted([q.pk for q in quotas])])
|
||||||
if rc.exists(f'quotas:availabilitycachewrite:{lock_name}'):
|
if rc.exists(f'quotas:availabilitycachewrite:{lock_name}{self._cache_key_suffix}'):
|
||||||
return
|
return
|
||||||
rc.setex(f'quotas:availabilitycachewrite:{lock_name}', '1', 10)
|
rc.setex(f'quotas:availabilitycachewrite:{lock_name}{self._cache_key_suffix}', '1', 10)
|
||||||
|
|
||||||
update = defaultdict(list)
|
update = defaultdict(list)
|
||||||
for q in quotas:
|
for q in quotas:
|
||||||
update[q.event_id].append(q)
|
update[q.event_id].append(q)
|
||||||
|
|
||||||
for eventid, quotas in update.items():
|
for eventid, quotas in update.items():
|
||||||
rc.hmset(f'quotas:{eventid}:availabilitycache', {
|
rc.hmset(f'quotas:{eventid}:availabilitycache{self._cache_key_suffix}', {
|
||||||
str(q.id): ",".join(
|
str(q.id): ",".join(
|
||||||
[str(i) for i in self.results[q]] +
|
[str(i) for i in self.results[q]] +
|
||||||
[str(int(time.time()))]
|
[str(int(time.time()))]
|
||||||
@@ -197,7 +200,7 @@ class QuotaAvailability:
|
|||||||
# To make sure old events do not fill up our redis instance, we set an expiry on the cache. However, we set it
|
# To make sure old events do not fill up our redis instance, we set an expiry on the cache. However, we set it
|
||||||
# on 7 days even though we mostly ignore values older than 2 monites. The reasoning is that we have some places
|
# on 7 days even though we mostly ignore values older than 2 monites. The reasoning is that we have some places
|
||||||
# where we set allow_cache_stale and use the old entries anyways to save on performance.
|
# where we set allow_cache_stale and use the old entries anyways to save on performance.
|
||||||
rc.expire(f'quotas:{eventid}:availabilitycache', 3600 * 24 * 7)
|
rc.expire(f'quotas:{eventid}:availabilitycache{self._cache_key_suffix}', 3600 * 24 * 7)
|
||||||
|
|
||||||
# We used to also delete item_quota_cache:* from the event cache here, but as the cache
|
# We used to also delete item_quota_cache:* from the event cache here, but as the cache
|
||||||
# gets more complex, this does not seem worth it. The cache is only present for up to
|
# gets more complex, this does not seem worth it. The cache is only present for up to
|
||||||
|
|||||||
@@ -259,6 +259,46 @@ DEFAULTS = {
|
|||||||
label=_("Gift card currency"),
|
label=_("Gift card currency"),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
'reusable_media_type_nfc_mf0aes': {
|
||||||
|
'default': 'False',
|
||||||
|
'type': bool,
|
||||||
|
'form_class': forms.BooleanField,
|
||||||
|
'serializer_class': serializers.BooleanField,
|
||||||
|
'form_kwargs': dict(
|
||||||
|
label=_("Active"),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
'reusable_media_type_nfc_mf0aes_autocreate_giftcard': {
|
||||||
|
'default': 'False',
|
||||||
|
'type': bool,
|
||||||
|
'form_class': forms.BooleanField,
|
||||||
|
'serializer_class': serializers.BooleanField,
|
||||||
|
'form_kwargs': dict(
|
||||||
|
label=_("Automatically create a new gift card if a new chip is encoded"),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
'reusable_media_type_nfc_mf0aes_autocreate_giftcard_currency': {
|
||||||
|
'default': 'EUR',
|
||||||
|
'type': str,
|
||||||
|
'form_class': forms.ChoiceField,
|
||||||
|
'serializer_class': serializers.ChoiceField,
|
||||||
|
'serializer_kwargs': dict(
|
||||||
|
choices=[(c.alpha_3, c.alpha_3 + " - " + c.name) for c in settings.CURRENCIES],
|
||||||
|
),
|
||||||
|
'form_kwargs': dict(
|
||||||
|
choices=[(c.alpha_3, c.alpha_3 + " - " + c.name) for c in settings.CURRENCIES],
|
||||||
|
label=_("Gift card currency"),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
'reusable_media_type_nfc_mf0aes_random_uid': {
|
||||||
|
'default': 'False',
|
||||||
|
'type': bool,
|
||||||
|
'form_class': forms.BooleanField,
|
||||||
|
'serializer_class': serializers.BooleanField,
|
||||||
|
'form_kwargs': dict(
|
||||||
|
label=_("Use UID protection feature of NFC chip"),
|
||||||
|
)
|
||||||
|
},
|
||||||
'max_items_per_order': {
|
'max_items_per_order': {
|
||||||
'default': '10',
|
'default': '10',
|
||||||
'type': int,
|
'type': int,
|
||||||
@@ -901,9 +941,9 @@ DEFAULTS = {
|
|||||||
'form_kwargs': dict(
|
'form_kwargs': dict(
|
||||||
label=_('Expiration delay'),
|
label=_('Expiration delay'),
|
||||||
help_text=_("The order will only actually expire this many days after the expiration date communicated "
|
help_text=_("The order will only actually expire this many days after the expiration date communicated "
|
||||||
"to the customer. However, this will not delay beyond the \"last date of payments\" "
|
"to the customer. If you select \"Only end payment terms on weekdays\" above, this will also "
|
||||||
"configured above, which is always enforced. The delay may also end on a weekend regardless "
|
"be respected. However, this will not delay beyond the \"last date of payments\" "
|
||||||
"of the other settings above."),
|
"configured above, which is always enforced."),
|
||||||
# Every order in between the official expiry date and the delayed expiry date has a performance penalty
|
# Every order in between the official expiry date and the delayed expiry date has a performance penalty
|
||||||
# for the cron job, so we limit this feature to 30 days to prevent arbitrary numbers of orders needing
|
# for the cron job, so we limit this feature to 30 days to prevent arbitrary numbers of orders needing
|
||||||
# to be checked.
|
# to be checked.
|
||||||
@@ -2301,6 +2341,24 @@ missing additional payment of **{pending_sum}**.
|
|||||||
You can view the payment information and the status of your order at
|
You can view the payment information and the status of your order at
|
||||||
{url}
|
{url}
|
||||||
|
|
||||||
|
Best regards,
|
||||||
|
Your {event} team""")) # noqa: W291
|
||||||
|
},
|
||||||
|
'mail_subject_order_payment_failed': {
|
||||||
|
'type': LazyI18nString,
|
||||||
|
'default': LazyI18nString.from_gettext(gettext_noop("Payment failed for your order: {code}")),
|
||||||
|
},
|
||||||
|
'mail_text_order_payment_failed': {
|
||||||
|
'type': LazyI18nString,
|
||||||
|
'default': LazyI18nString.from_gettext(gettext_noop("""Hello,
|
||||||
|
|
||||||
|
your payment attempt for your order for {event} has failed.
|
||||||
|
|
||||||
|
Your order is still valid and you can try to pay again using the same or a different payment method. Please complete your payment before {expire_date}.
|
||||||
|
|
||||||
|
You can retry the payment and view the status of your order at
|
||||||
|
{url}
|
||||||
|
|
||||||
Best regards,
|
Best regards,
|
||||||
Your {event} team""")) # noqa: W291
|
Your {event} team""")) # noqa: W291
|
||||||
},
|
},
|
||||||
@@ -3657,14 +3715,10 @@ def validate_organizer_settings(organizer, settings_dict):
|
|||||||
# This is not doing anything for the time being.
|
# This is not doing anything for the time being.
|
||||||
# But earlier we called validate_event_settings for the organizer, too - and that didn't do anything for
|
# But earlier we called validate_event_settings for the organizer, too - and that didn't do anything for
|
||||||
# organizer-settings either.
|
# organizer-settings either.
|
||||||
#
|
if settings_dict.get('reusable_media_type_nfc_mf0aes') and settings_dict.get('reusable_media_type_nfc_uid'):
|
||||||
# N.B.: When actually fleshing out this stub, adding it to the OrganizerUpdateForm should be considered.
|
|
||||||
"""
|
|
||||||
if settings_dict.get('reusable_media_type_ntag_pretix1') and settings_dict.get('reusable_media_type_nfc_uid'):
|
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
'reusable_media_type_nfc_uid': _('This needs to be disabled if other NFC-based types are active.')
|
'reusable_media_type_nfc_uid': _('This needs to be disabled if other NFC-based types are active.')
|
||||||
})
|
})
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def global_settings_object(holder):
|
def global_settings_object(holder):
|
||||||
|
|||||||
@@ -683,12 +683,16 @@ dictionaries as values that contain keys like in the following example::
|
|||||||
"product": {
|
"product": {
|
||||||
"label": _("Product name"),
|
"label": _("Product name"),
|
||||||
"editor_sample": _("Sample product"),
|
"editor_sample": _("Sample product"),
|
||||||
"evaluate": lambda orderposition, order, event: str(orderposition.item)
|
"evaluate": lambda orderposition, order, event: str(orderposition.item),
|
||||||
|
"evaluate_bulk": lambda orderpositions: [str(op.item) for op in orderpositions],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
The ``evaluate`` member will be called with the order position, order and event as arguments. The event might
|
The ``evaluate`` member will be called with the order position, order and event as arguments. The event might
|
||||||
also be a subevent, if applicable.
|
also be a subevent, if applicable.
|
||||||
|
|
||||||
|
The ``evaluate_bulk`` member is optional but can significantly improve performance in some situations because you
|
||||||
|
can perform database fetches in bulk instead of single queries for every position.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@@ -787,3 +791,23 @@ return a dictionary mapping names of attributes in the settings store to DRF ser
|
|||||||
|
|
||||||
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
|
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
customer_created = GlobalSignal()
|
||||||
|
"""
|
||||||
|
Arguments: ``customer``
|
||||||
|
|
||||||
|
This signal is sent out every time a customer account is created. The ``customer``
|
||||||
|
object is given as the first argument.
|
||||||
|
|
||||||
|
The ``sender`` keyword argument will contain the organizer.
|
||||||
|
"""
|
||||||
|
|
||||||
|
customer_signed_in = GlobalSignal()
|
||||||
|
"""
|
||||||
|
Arguments: ``customer``
|
||||||
|
|
||||||
|
This signal is sent out every time a customer signs in. The ``customer`` object
|
||||||
|
is given as the first argument.
|
||||||
|
|
||||||
|
The ``sender`` keyword argument will contain the organizer.
|
||||||
|
"""
|
||||||
|
|||||||
@@ -1116,6 +1116,16 @@ class MailSettingsForm(SettingsForm):
|
|||||||
help_text=_("This email only applies to payment methods that can receive incomplete payments, "
|
help_text=_("This email only applies to payment methods that can receive incomplete payments, "
|
||||||
"such as bank transfer."),
|
"such as bank transfer."),
|
||||||
)
|
)
|
||||||
|
mail_subject_order_payment_failed = I18nFormField(
|
||||||
|
label=_("Subject"),
|
||||||
|
required=False,
|
||||||
|
widget=I18nTextInput,
|
||||||
|
)
|
||||||
|
mail_text_order_payment_failed = I18nFormField(
|
||||||
|
label=_("Text"),
|
||||||
|
required=False,
|
||||||
|
widget=I18nTextarea,
|
||||||
|
)
|
||||||
mail_subject_waiting_list = I18nFormField(
|
mail_subject_waiting_list = I18nFormField(
|
||||||
label=_("Subject"),
|
label=_("Subject"),
|
||||||
required=False,
|
required=False,
|
||||||
@@ -1289,6 +1299,8 @@ class MailSettingsForm(SettingsForm):
|
|||||||
'mail_subject_order_pending_warning': ['event', 'order'],
|
'mail_subject_order_pending_warning': ['event', 'order'],
|
||||||
'mail_text_order_incomplete_payment': ['event', 'order', 'pending_sum'],
|
'mail_text_order_incomplete_payment': ['event', 'order', 'pending_sum'],
|
||||||
'mail_subject_order_incomplete_payment': ['event', 'order'],
|
'mail_subject_order_incomplete_payment': ['event', 'order'],
|
||||||
|
'mail_text_order_payment_failed': ['event', 'order'],
|
||||||
|
'mail_subject_order_payment_failed': ['event', 'order'],
|
||||||
'mail_text_order_custom_mail': ['event', 'order'],
|
'mail_text_order_custom_mail': ['event', 'order'],
|
||||||
'mail_text_download_reminder': ['event', 'order'],
|
'mail_text_download_reminder': ['event', 'order'],
|
||||||
'mail_subject_download_reminder': ['event', 'order'],
|
'mail_subject_download_reminder': ['event', 'order'],
|
||||||
|
|||||||
@@ -1732,8 +1732,8 @@ class CheckinListAttendeeFilterForm(FilterForm):
|
|||||||
'-timestamp': (OrderBy(F('last_entry'), nulls_last=True, descending=True), '-order__code'),
|
'-timestamp': (OrderBy(F('last_entry'), nulls_last=True, descending=True), '-order__code'),
|
||||||
'item': ('item__name', 'variation__value', 'order__code'),
|
'item': ('item__name', 'variation__value', 'order__code'),
|
||||||
'-item': ('-item__name', '-variation__value', '-order__code'),
|
'-item': ('-item__name', '-variation__value', '-order__code'),
|
||||||
'seat': ('seat__sorting_rank', 'seat__guid'),
|
'seat': ('seat__sorting_rank', 'seat__seat_guid'),
|
||||||
'-seat': ('-seat__sorting_rank', '-seat__guid'),
|
'-seat': ('-seat__sorting_rank', '-seat__seat_guid'),
|
||||||
'date': ('subevent__date_from', 'subevent__id', 'order__code'),
|
'date': ('subevent__date_from', 'subevent__id', 'order__code'),
|
||||||
'-date': ('-subevent__date_from', 'subevent__id', '-order__code'),
|
'-date': ('-subevent__date_from', 'subevent__id', '-order__code'),
|
||||||
'name': {'_order': F('display_name').asc(nulls_first=True),
|
'name': {'_order': F('display_name').asc(nulls_first=True),
|
||||||
@@ -1940,7 +1940,7 @@ class VoucherFilterForm(FilterForm):
|
|||||||
'item__category__position',
|
'item__category__position',
|
||||||
'item__category',
|
'item__category',
|
||||||
'item__position',
|
'item__position',
|
||||||
'item__variation__position',
|
'variation__position',
|
||||||
'quota__name',
|
'quota__name',
|
||||||
),
|
),
|
||||||
'subevent': 'subevent__date_from',
|
'subevent': 'subevent__date_from',
|
||||||
@@ -1950,7 +1950,7 @@ class VoucherFilterForm(FilterForm):
|
|||||||
'-item__category__position',
|
'-item__category__position',
|
||||||
'-item__category',
|
'-item__category',
|
||||||
'-item__position',
|
'-item__position',
|
||||||
'-item__variation__position',
|
'-variation__position',
|
||||||
'-quota__name',
|
'-quota__name',
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -461,11 +461,6 @@ class ItemCreateForm(I18nModelForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if self.cleaned_data.get('copy_from'):
|
if self.cleaned_data.get('copy_from'):
|
||||||
for mv in self.cleaned_data['copy_from'].meta_values.all():
|
|
||||||
mv.pk = None
|
|
||||||
mv.item = instance
|
|
||||||
mv.save(force_insert=True)
|
|
||||||
|
|
||||||
for question in self.cleaned_data['copy_from'].questions.all():
|
for question in self.cleaned_data['copy_from'].questions.all():
|
||||||
question.items.add(instance)
|
question.items.add(instance)
|
||||||
question.log_action('pretix.event.question.changed', user=self.user, data={
|
question.log_action('pretix.event.question.changed', user=self.user, data={
|
||||||
|
|||||||
@@ -412,6 +412,10 @@ class OrganizerSettingsForm(SettingsForm):
|
|||||||
'reusable_media_type_nfc_uid',
|
'reusable_media_type_nfc_uid',
|
||||||
'reusable_media_type_nfc_uid_autocreate_giftcard',
|
'reusable_media_type_nfc_uid_autocreate_giftcard',
|
||||||
'reusable_media_type_nfc_uid_autocreate_giftcard_currency',
|
'reusable_media_type_nfc_uid_autocreate_giftcard_currency',
|
||||||
|
'reusable_media_type_nfc_mf0aes',
|
||||||
|
'reusable_media_type_nfc_mf0aes_autocreate_giftcard',
|
||||||
|
'reusable_media_type_nfc_mf0aes_autocreate_giftcard_currency',
|
||||||
|
'reusable_media_type_nfc_mf0aes_random_uid',
|
||||||
]
|
]
|
||||||
|
|
||||||
organizer_logo_image = ExtFileField(
|
organizer_logo_image = ExtFileField(
|
||||||
|
|||||||
@@ -341,6 +341,7 @@ def pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs):
|
|||||||
'pretix.giftcards.acceptance.added': _('Gift card acceptance for another organizer has been added.'),
|
'pretix.giftcards.acceptance.added': _('Gift card acceptance for another organizer has been added.'),
|
||||||
'pretix.giftcards.acceptance.removed': _('Gift card acceptance for another organizer has been removed.'),
|
'pretix.giftcards.acceptance.removed': _('Gift card acceptance for another organizer has been removed.'),
|
||||||
'pretix.giftcards.acceptance.acceptor.invited': _('A new gift card acceptor has been invited.'),
|
'pretix.giftcards.acceptance.acceptor.invited': _('A new gift card acceptor has been invited.'),
|
||||||
|
'pretix.giftcards.acceptance.acceptor.removed': _('A gift card acceptor has been removed.'),
|
||||||
'pretix.giftcards.acceptance.issuer.removed': _('A gift card issuer has been removed or declined.'),
|
'pretix.giftcards.acceptance.issuer.removed': _('A gift card issuer has been removed or declined.'),
|
||||||
'pretix.giftcards.acceptance.issuer.accepted': _('A new gift card issuer has been accepted.'),
|
'pretix.giftcards.acceptance.issuer.accepted': _('A new gift card issuer has been accepted.'),
|
||||||
'pretix.webhook.created': _('The webhook has been created.'),
|
'pretix.webhook.created': _('The webhook has been created.'),
|
||||||
@@ -368,6 +369,7 @@ def pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs):
|
|||||||
'pretix.reusable_medium.created.auto': _('The reusable medium has been created automatically.'),
|
'pretix.reusable_medium.created.auto': _('The reusable medium has been created automatically.'),
|
||||||
'pretix.reusable_medium.changed': _('The reusable medium has been changed.'),
|
'pretix.reusable_medium.changed': _('The reusable medium has been changed.'),
|
||||||
'pretix.reusable_medium.linked_orderposition.changed': _('The medium has been connected to a new ticket.'),
|
'pretix.reusable_medium.linked_orderposition.changed': _('The medium has been connected to a new ticket.'),
|
||||||
|
'pretix.reusable_medium.linked_giftcard.changed': _('The medium has been connected to a new gift card.'),
|
||||||
'pretix.email.error': _('Sending of an email has failed.'),
|
'pretix.email.error': _('Sending of an email has failed.'),
|
||||||
'pretix.event.comment': _('The event\'s internal comment has been updated.'),
|
'pretix.event.comment': _('The event\'s internal comment has been updated.'),
|
||||||
'pretix.event.canceled': _('The event has been canceled.'),
|
'pretix.event.canceled': _('The event has been canceled.'),
|
||||||
@@ -432,6 +434,7 @@ def pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs):
|
|||||||
'the order has been received and requires '
|
'the order has been received and requires '
|
||||||
'approval.'),
|
'approval.'),
|
||||||
'pretix.event.order.email.resend': _('An email with a link to the order detail page has been resent to the user.'),
|
'pretix.event.order.email.resend': _('An email with a link to the order detail page has been resent to the user.'),
|
||||||
|
'pretix.event.order.email.payment_failed': _('An email has been sent to notify the user that the payment failed.'),
|
||||||
'pretix.event.order.payment.confirmed': _('Payment {local_id} has been confirmed.'),
|
'pretix.event.order.payment.confirmed': _('Payment {local_id} has been confirmed.'),
|
||||||
'pretix.event.order.payment.canceled': _('Payment {local_id} has been canceled.'),
|
'pretix.event.order.payment.canceled': _('Payment {local_id} has been canceled.'),
|
||||||
'pretix.event.order.payment.canceled.failed': _('Canceling payment {local_id} has failed.'),
|
'pretix.event.order.payment.canceled.failed': _('Canceling payment {local_id} has failed.'),
|
||||||
|
|||||||
@@ -304,6 +304,10 @@ an instance of a form class that you bind yourself when appropriate. Your form w
|
|||||||
as part of the standard validation and rendering cycle and rendered using default bootstrap
|
as part of the standard validation and rendering cycle and rendered using default bootstrap
|
||||||
styles. It is advisable to set a prefix for your form to avoid clashes with other plugins.
|
styles. It is advisable to set a prefix for your form to avoid clashes with other plugins.
|
||||||
|
|
||||||
|
Your forms may also have two special properties: ``template`` with a template that will be
|
||||||
|
included to render the form, and ``title``, which will be used as a headline. Your template
|
||||||
|
will be passed a ``form`` variable with your form.
|
||||||
|
|
||||||
As with all plugin signals, the ``sender`` keyword argument will contain the event.
|
As with all plugin signals, the ``sender`` keyword argument will contain the event.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="panel-body form-horizontal">
|
<div class="panel-body form-horizontal">
|
||||||
{% if spf_warning %}
|
{% if spf_warning %}
|
||||||
<div class="alert alert-warning">
|
<div class="alert alert-danger">
|
||||||
<p>
|
<p>
|
||||||
{{ spf_warning }}
|
{{ spf_warning }}
|
||||||
</p>
|
</p>
|
||||||
@@ -70,10 +70,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group submit-group">
|
{% if spf_warning %}
|
||||||
<button type="submit" class="btn btn-primary btn-save">
|
<div class="form-group submit-group">
|
||||||
{% trans "Save" %}
|
<a href="" class="btn btn-default btn-save">
|
||||||
</button>
|
{% trans "Cancel" %}
|
||||||
</div>
|
</a>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="form-group submit-group">
|
||||||
|
<button type="submit" class="btn btn-primary btn-save">
|
||||||
|
{% trans "Save" %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
<ul class="dropdown-menu dropdown-menu-right">
|
||||||
|
<li>
|
||||||
|
<a href="{% url "control:event.qrcode" event=request.event.slug organizer=request.organizer.slug filetype="png" %}{% if url %}?url={{ url|urlencode }}{% endif %}"
|
||||||
|
target="_blank" download>
|
||||||
|
{% blocktrans with filetype="PNG" %}Download QR code as {{ filetype }} image{% endblocktrans %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="{% url "control:event.qrcode" event=request.event.slug organizer=request.organizer.slug filetype="svg" %}{% if url %}?url={{ url|urlencode }}{% endif %}"
|
||||||
|
target="_blank" download>
|
||||||
|
{% blocktrans with filetype="SVG" %}Download QR code as {{ filetype }} image{% endblocktrans %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="{% url "control:event.qrcode" event=request.event.slug organizer=request.organizer.slug filetype="jpeg" %}{% if url %}?url={{ url|urlencode }}{% endif %}"
|
||||||
|
target="_blank" download>
|
||||||
|
{% blocktrans with filetype="JPG" %}Download QR code as {{ filetype }} image{% endblocktrans %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="{% url "control:event.qrcode" event=request.event.slug organizer=request.organizer.slug filetype="gif" %}{% if url %}?url={{ url|urlencode }}{% endif %}"
|
||||||
|
target="_blank" download>
|
||||||
|
{% blocktrans with filetype="GIF" %}Download QR code as {{ filetype }} image{% endblocktrans %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
@@ -27,28 +27,7 @@
|
|||||||
<button type="button" class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown" title="{% trans "Create QR code" %}" aria-haspopup="true" aria-expanded="false">
|
<button type="button" class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown" title="{% trans "Create QR code" %}" aria-haspopup="true" aria-expanded="false">
|
||||||
<i class="fa fa-qrcode" aria-hidden="true"></i>
|
<i class="fa fa-qrcode" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu dropdown-menu-right">
|
{% include "pretixcontrol/event/fragment_qr_dropdown.html" with url=0 %}
|
||||||
<li>
|
|
||||||
<a href="{% url "control:event.qrcode" event=request.event.slug organizer=request.organizer.slug filetype="png" %}" target="_blank" download>
|
|
||||||
{% blocktrans with filetype="PNG" %}Download QR code as {{ filetype }} image{% endblocktrans %}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="{% url "control:event.qrcode" event=request.event.slug organizer=request.organizer.slug filetype="svg" %}" target="_blank" download>
|
|
||||||
{% blocktrans with filetype="SVG" %}Download QR code as {{ filetype }} image{% endblocktrans %}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="{% url "control:event.qrcode" event=request.event.slug organizer=request.organizer.slug filetype="jpeg" %}" target="_blank" download>
|
|
||||||
{% blocktrans with filetype="JPG" %}Download QR code as {{ filetype }} image{% endblocktrans %}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="{% url "control:event.qrcode" event=request.event.slug organizer=request.organizer.slug filetype="gif" %}" target="_blank" download>
|
|
||||||
{% blocktrans with filetype="GIF" %}Download QR code as {{ filetype }} image{% endblocktrans %}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -105,6 +105,9 @@
|
|||||||
{% blocktrans asvar title_payment_reminder %}Payment reminder{% endblocktrans %}
|
{% blocktrans asvar title_payment_reminder %}Payment reminder{% endblocktrans %}
|
||||||
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="order_expirew" title=title_payment_reminder items="mail_days_order_expire_warning,mail_subject_order_expire_warning,mail_text_order_expire_warning,mail_subject_order_pending_warning,mail_text_order_pending_warning,mail_subject_order_incomplete_payment,mail_text_order_incomplete_payment" exclude="mail_days_order_expire_warning" %}
|
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="order_expirew" title=title_payment_reminder items="mail_days_order_expire_warning,mail_subject_order_expire_warning,mail_text_order_expire_warning,mail_subject_order_pending_warning,mail_text_order_pending_warning,mail_subject_order_incomplete_payment,mail_text_order_incomplete_payment" exclude="mail_days_order_expire_warning" %}
|
||||||
|
|
||||||
|
{% blocktrans asvar title_payment_failed %}Payment failed{% endblocktrans %}
|
||||||
|
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="payment_failed" title=title_payment_failed items="mail_subject_order_payment_failed,mail_text_order_payment_failed" %}
|
||||||
|
|
||||||
{% blocktrans asvar title_waiting_list_notification %}Waiting list notification{% endblocktrans %}
|
{% blocktrans asvar title_waiting_list_notification %}Waiting list notification{% endblocktrans %}
|
||||||
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="waiting_list" title=title_waiting_list_notification items="mail_subject_waiting_list,mail_text_waiting_list" %}
|
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="waiting_list" title=title_waiting_list_notification items="mail_subject_waiting_list,mail_text_waiting_list" %}
|
||||||
|
|
||||||
|
|||||||
@@ -337,7 +337,7 @@
|
|||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
{% blocktrans trimmed %}
|
{% blocktrans trimmed %}
|
||||||
The waiting list currently is not compatible with some advanced features of pretix such as
|
The waiting list currently is not compatible with some advanced features of pretix such as
|
||||||
add-on products or product bundles.
|
hidden products, add-on products or product bundles.
|
||||||
{% endblocktrans %}
|
{% endblocktrans %}
|
||||||
</div>
|
</div>
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
|
|||||||
@@ -186,8 +186,12 @@
|
|||||||
{% bootstrap_field form.media_type layout="control" %}
|
{% bootstrap_field form.media_type layout="control" %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% for f in plugin_forms %}
|
{% for f in plugin_forms %}
|
||||||
{% if f.is_layouts %}
|
{% if f.is_layouts and not f.title %}
|
||||||
{% bootstrap_form f layout="control" %}
|
{% if f.template and not "template" in f.fields %}
|
||||||
|
{% include f.template with form=f %}
|
||||||
|
{% else %}
|
||||||
|
{% bootstrap_form f layout="control" %}
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
@@ -256,11 +260,27 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% bootstrap_field form.show_quota_left layout="control" %}
|
{% bootstrap_field form.show_quota_left layout="control" %}
|
||||||
{% for f in plugin_forms %}
|
{% for f in plugin_forms %}
|
||||||
{% if not f.is_layouts %}
|
{% if not f.is_layouts and not f.title %}
|
||||||
{% bootstrap_form f layout="control" %}
|
{% if f.template and not "template" in f.fields %}
|
||||||
|
{% include f.template with form=f %}
|
||||||
|
{% else %}
|
||||||
|
{% bootstrap_form f layout="control" %}
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
{% for f in plugin_forms %}
|
||||||
|
{% if not f.is_layouts and f.title %}
|
||||||
|
<fieldset>
|
||||||
|
<legend>{{ f.title }}</legend>
|
||||||
|
{% if f.template and not "template" in f.fields %}
|
||||||
|
{% include f.template with form=f %}
|
||||||
|
{% else %}
|
||||||
|
{% bootstrap_form f layout="control" %}
|
||||||
|
{% endif %}
|
||||||
|
</fieldset>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group submit-group">
|
<div class="form-group submit-group">
|
||||||
<button type="submit" class="btn btn-primary btn-save">
|
<button type="submit" class="btn btn-primary btn-save">
|
||||||
|
|||||||
@@ -69,7 +69,7 @@
|
|||||||
<td></td>
|
<td></td>
|
||||||
<td class="text-right flip">
|
<td class="text-right flip">
|
||||||
<strong>
|
<strong>
|
||||||
{{ sums.count }}
|
{{ sums.sum_count }}
|
||||||
</strong>
|
</strong>
|
||||||
</td>
|
</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
|
|||||||
@@ -262,6 +262,32 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h4 class="panel-title">NFC Mifare Ultralight AES</h4>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<p class="help-block">
|
||||||
|
{% blocktrans trimmed %}
|
||||||
|
This medium type works only with NFC chips of the type Mifare Ultralight AES
|
||||||
|
made by NXP. This provides a higher level of security than other approaches, but
|
||||||
|
requires all chips to be encoded prior to use.
|
||||||
|
{% endblocktrans %}
|
||||||
|
{% blocktrans trimmed %}
|
||||||
|
NFC media can currently only be connected to gift cards.
|
||||||
|
{% endblocktrans %}
|
||||||
|
</p>
|
||||||
|
{% bootstrap_field sform.reusable_media_type_nfc_mf0aes layout="control" %}
|
||||||
|
<div data-display-dependency="#{{ sform.reusable_media_type_nfc_mf0aes.id_for_label }}">
|
||||||
|
{% bootstrap_field sform.reusable_media_type_nfc_mf0aes_autocreate_giftcard layout="control" %}
|
||||||
|
<div data-display-dependency="#{{ sform.reusable_media_type_nfc_mf0aes_autocreate_giftcard.id_for_label }}">
|
||||||
|
{% bootstrap_field sform.reusable_media_type_nfc_mf0aes_autocreate_giftcard_currency layout="control" %}
|
||||||
|
</div>
|
||||||
|
{% bootstrap_field sform.reusable_media_type_nfc_mf0aes_random_uid layout="control" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
@@ -269,6 +295,11 @@
|
|||||||
{% bootstrap_field sform.invoice_regenerate_allowed layout="control" %}
|
{% bootstrap_field sform.invoice_regenerate_allowed layout="control" %}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group submit-group">
|
||||||
|
<button type="submit" class="btn btn-primary btn-save">
|
||||||
|
{% trans "Save" %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-12 col-lg-2">
|
<div class="col-xs-12 col-lg-2">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
@@ -281,10 +312,5 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group submit-group">
|
|
||||||
<button type="submit" class="btn btn-primary btn-save">
|
|
||||||
{% trans "Save" %}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
placeholder="{% trans "Prefix (optional)" %}">
|
placeholder="{% trans "Prefix (optional)" %}">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="number" class="form-control input-xs"
|
<input type="number" class="form-control input-xs"
|
||||||
id="voucher-bulk-codes-num"
|
id="voucher-bulk-codes-num" max="100000"
|
||||||
placeholder="{% trans "Number" context "number_of_things" %}">
|
placeholder="{% trans "Number" context "number_of_things" %}">
|
||||||
<div class="input-group-btn">
|
<div class="input-group-btn">
|
||||||
<button class="btn btn-default" type="button" id="voucher-bulk-codes-generate"
|
<button class="btn btn-default" type="button" id="voucher-bulk-codes-generate"
|
||||||
|
|||||||
@@ -42,10 +42,18 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-md-3 control-label" for="id_url">{% trans "Voucher link" %}</label>
|
<label class="col-md-3 control-label" for="id_url">{% trans "Voucher link" %}</label>
|
||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
<input type="text" name="url"
|
<div class="input-group">
|
||||||
value="{% abseventurl request.event "presale:event.redeem" %}?voucher={{ voucher.code|urlencode }}{% if voucher.subevent_id %}&subevent={{ voucher.subevent_id }}{% endif %}"
|
<input type="text" name="url"
|
||||||
class="form-control"
|
value="{{ url }}"
|
||||||
id="id_url" readonly>
|
class="form-control"
|
||||||
|
id="id_url" readonly>
|
||||||
|
<div class="input-group-btn">
|
||||||
|
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" title="{% trans "Create QR code" %}" aria-haspopup="true" aria-expanded="false">
|
||||||
|
<i class="fa fa-qrcode" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
{% include "pretixcontrol/event/fragment_qr_dropdown.html" with url=url %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -96,7 +96,9 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>
|
<th>
|
||||||
{% if "can_change_vouchers" in request.eventpermset %}
|
{% if "can_change_vouchers" in request.eventpermset %}
|
||||||
<input type="checkbox" data-toggle-table />
|
<label aria-label="{% trans "select all rows for batch-operation" %}" class="batch-select-label">
|
||||||
|
<input type="checkbox" data-toggle-table />
|
||||||
|
</label>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
@@ -139,7 +141,9 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
{% if "can_change_vouchers" in request.eventpermset %}
|
{% if "can_change_vouchers" in request.eventpermset %}
|
||||||
<input type="checkbox" name="voucher" class="" value="{{ v.pk }}"/>
|
<label aria-label="{% trans "select row for batch-operation" %}" class="batch-select-label">
|
||||||
|
<input type="checkbox" name="voucher" class="batch-select-checkbox" value="{{ v.pk }}"/>
|
||||||
|
</label>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@@ -194,9 +198,12 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
{% if "can_change_vouchers" in request.eventpermset %}
|
{% if "can_change_vouchers" in request.eventpermset %}
|
||||||
<button type="submit" class="btn btn-default btn-save" name="action" value="delete">
|
<div class="batch-select-actions">
|
||||||
{% trans "Delete selected" %}
|
<button type="submit" class="btn btn-danger" name="action" value="delete">
|
||||||
</button>
|
<i class="fa fa-trash" aria-hidden="true"></i>
|
||||||
|
{% trans "Delete selected" %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</form>
|
</form>
|
||||||
{% include "pretixcontrol/pagination.html" %}
|
{% include "pretixcontrol/pagination.html" %}
|
||||||
|
|||||||
@@ -212,11 +212,21 @@
|
|||||||
<span class="label label-warning">{% trans "Voucher assigned" %}</span>
|
<span class="label label-warning">{% trans "Voucher assigned" %}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% elif e.availability.0 == 100 %}
|
{% elif e.availability.0 == 100 %}
|
||||||
<span class="label label-warning">
|
{% if e.availability.1|default_if_none:"none" == "none" %}
|
||||||
{% blocktrans with num=e.availability.1 %}
|
<span class="label label-danger" data-toggle="tooltip"
|
||||||
Waiting, product {{ num }}x available
|
title="{% trans "For safety reasons, the waiting list does not run if the quota is set to unlimited." %}">
|
||||||
{% endblocktrans %}
|
<span class="fa fa-ban" aria-hidden="true"></span>
|
||||||
</span>
|
{% blocktrans trimmed %}
|
||||||
|
Quota unlimited
|
||||||
|
{% endblocktrans %}
|
||||||
|
</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="label label-warning">
|
||||||
|
{% blocktrans with num=e.availability.1 %}
|
||||||
|
Waiting, product {{ num }}x available
|
||||||
|
{% endblocktrans %}
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="label label-danger">{% trans "Waiting, product unavailable" %}</span>
|
<span class="label label-danger">{% trans "Waiting, product unavailable" %}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -226,7 +236,7 @@
|
|||||||
<a href="{% url "control:event.voucher" organizer=request.event.organizer.slug event=request.event.slug voucher=e.voucher.pk %}">
|
<a href="{% url "control:event.voucher" organizer=request.event.organizer.slug event=request.event.slug voucher=e.voucher.pk %}">
|
||||||
{{ e.voucher }}
|
{{ e.voucher }}
|
||||||
</a>
|
</a>
|
||||||
{% elif not e.voucher and e.availability.0 == 100 %}
|
{% elif not e.voucher and e.availability.0 == 100 and e.availability.1|default_if_none:"none" != "none" %}
|
||||||
<button name="assign" value="{{ e.pk }}" class="btn btn-default btn-xs">
|
<button name="assign" value="{{ e.pk }}" class="btn btn-default btn-xs">
|
||||||
{% trans "Send a voucher" %}
|
{% trans "Send a voucher" %}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -530,7 +530,8 @@ class CheckInListSimulator(EventPermissionRequiredMixin, FormView):
|
|||||||
and (self.result["status"] in ("ok", "incomplete") or self.result["reason"] == "rules"):
|
and (self.result["status"] in ("ok", "incomplete") or self.result["reason"] == "rules"):
|
||||||
op = OrderPosition.objects.get(pk=self.result["position"]["id"])
|
op = OrderPosition.objects.get(pk=self.result["position"]["id"])
|
||||||
rule_data = LazyRuleVars(op, self.list, form.cleaned_data["datetime"])
|
rule_data = LazyRuleVars(op, self.list, form.cleaned_data["datetime"])
|
||||||
rule_graph = _logic_annotate_for_graphic_explain(self.list.rules, op.subevent or self.list.event, rule_data)
|
rule_graph = _logic_annotate_for_graphic_explain(self.list.rules, op.subevent or self.list.event, rule_data,
|
||||||
|
form.cleaned_data["datetime"])
|
||||||
self.result["rule_graph"] = rule_graph
|
self.result["rule_graph"] = rule_graph
|
||||||
|
|
||||||
if self.result.get("questions"):
|
if self.result.get("questions"):
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ from collections import OrderedDict
|
|||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from itertools import groupby
|
from itertools import groupby
|
||||||
from urllib.parse import urlsplit
|
from urllib.parse import urlparse, urlsplit
|
||||||
from zoneinfo import ZoneInfo
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
import bleach
|
import bleach
|
||||||
@@ -50,6 +50,7 @@ from django.apps import apps
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.core.files import File
|
from django.core.files import File
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models import ProtectedError
|
from django.db.models import ProtectedError
|
||||||
@@ -61,6 +62,7 @@ from django.http import (
|
|||||||
from django.shortcuts import get_object_or_404, redirect
|
from django.shortcuts import get_object_or_404, redirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
|
from django.utils.http import url_has_allowed_host_and_scheme
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.utils.translation import gettext, gettext_lazy as _, gettext_noop
|
from django.utils.translation import gettext, gettext_lazy as _, gettext_noop
|
||||||
from django.views.generic import FormView, ListView
|
from django.views.generic import FormView, ListView
|
||||||
@@ -1530,6 +1532,12 @@ class EventQRCode(EventPermissionRequiredMixin, View):
|
|||||||
def get(self, request, *args, filetype, **kwargs):
|
def get(self, request, *args, filetype, **kwargs):
|
||||||
url = build_absolute_uri(request.event, 'presale:event.index')
|
url = build_absolute_uri(request.event, 'presale:event.index')
|
||||||
|
|
||||||
|
if "url" in request.GET:
|
||||||
|
if url_has_allowed_host_and_scheme(request.GET["url"], allowed_hosts=[urlparse(url).netloc]):
|
||||||
|
url = request.GET["url"]
|
||||||
|
else:
|
||||||
|
raise PermissionDenied("Untrusted URL")
|
||||||
|
|
||||||
qr = qrcode.QRCode(
|
qr = qrcode.QRCode(
|
||||||
version=1,
|
version=1,
|
||||||
error_correction=qrcode.constants.ERROR_CORRECT_M,
|
error_correction=qrcode.constants.ERROR_CORRECT_M,
|
||||||
|
|||||||
@@ -88,6 +88,10 @@ from ...helpers.compat import CompatDeleteView
|
|||||||
from . import ChartContainingView, CreateView, PaginationMixin, UpdateView
|
from . import ChartContainingView, CreateView, PaginationMixin, UpdateView
|
||||||
|
|
||||||
|
|
||||||
|
def has_truthy_attr(cls, attr):
|
||||||
|
return hasattr(cls, attr) and getattr(cls, attr)
|
||||||
|
|
||||||
|
|
||||||
class ItemList(ListView):
|
class ItemList(ListView):
|
||||||
model = Item
|
model = Item
|
||||||
context_object_name = 'items'
|
context_object_name = 'items'
|
||||||
@@ -1184,30 +1188,46 @@ class MetaDataEditorMixin:
|
|||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def meta_forms(self):
|
def meta_forms(self):
|
||||||
if hasattr(self, 'object') and self.object:
|
if getattr(self, 'object', None):
|
||||||
val_instances = {
|
val_instances = {
|
||||||
v.property_id: v for v in self.object.meta_values.all()
|
v.property_id: v for v in self.object.meta_values.all()
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
val_instances = {}
|
val_instances = {}
|
||||||
|
|
||||||
|
if getattr(self, 'copy_from', None):
|
||||||
|
defaults = {
|
||||||
|
v.property_id: v.value for v in self.copy_from.meta_values.all()
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
defaults = {}
|
||||||
|
|
||||||
formlist = []
|
formlist = []
|
||||||
|
|
||||||
for p in self.request.event.item_meta_properties.all():
|
for p in self.request.event.item_meta_properties.all():
|
||||||
formlist.append(self._make_meta_form(p, val_instances))
|
formlist.append(self._make_meta_form(p, val_instances, defaults))
|
||||||
return formlist
|
return formlist
|
||||||
|
|
||||||
def _make_meta_form(self, p, val_instances):
|
def _make_meta_form(self, p, val_instances, defaults):
|
||||||
return self.meta_form(
|
return self.meta_form(
|
||||||
prefix='prop-{}'.format(p.pk),
|
prefix='prop-{}'.format(p.pk),
|
||||||
property=p,
|
property=p,
|
||||||
instance=val_instances.get(p.pk, self.meta_model(property=p, item=self.object)),
|
instance=val_instances.get(
|
||||||
|
p.pk,
|
||||||
|
self.meta_model(
|
||||||
|
property=p,
|
||||||
|
item=self.object if getattr(self, 'object', None) else None,
|
||||||
|
value=defaults.get(p.pk, None)
|
||||||
|
)
|
||||||
|
),
|
||||||
data=(self.request.POST if self.request.method == "POST" else None)
|
data=(self.request.POST if self.request.method == "POST" else None)
|
||||||
)
|
)
|
||||||
|
|
||||||
def save_meta(self):
|
def save_meta(self):
|
||||||
for f in self.meta_forms:
|
for f in self.meta_forms:
|
||||||
if f.cleaned_data.get('value'):
|
if f.cleaned_data.get('value'):
|
||||||
|
if not f.instance.item_id:
|
||||||
|
f.instance.item = self.object
|
||||||
f.save()
|
f.save()
|
||||||
elif f.instance and f.instance.pk:
|
elif f.instance and f.instance.pk:
|
||||||
f.instance.delete()
|
f.instance.delete()
|
||||||
@@ -1253,6 +1273,7 @@ class ItemCreate(EventPermissionRequiredMixin, MetaDataEditorMixin, CreateView):
|
|||||||
messages.success(self.request, _('Your changes have been saved.'))
|
messages.success(self.request, _('Your changes have been saved.'))
|
||||||
|
|
||||||
ret = super().form_valid(form)
|
ret = super().form_valid(form)
|
||||||
|
self.save_meta()
|
||||||
form.instance.log_action('pretix.event.item.added', user=self.request.user, data={
|
form.instance.log_action('pretix.event.item.added', user=self.request.user, data={
|
||||||
k: (form.cleaned_data.get(k).name
|
k: (form.cleaned_data.get(k).name
|
||||||
if isinstance(form.cleaned_data.get(k), File)
|
if isinstance(form.cleaned_data.get(k), File)
|
||||||
@@ -1279,6 +1300,14 @@ class ItemCreate(EventPermissionRequiredMixin, MetaDataEditorMixin, CreateView):
|
|||||||
ctx['meta_forms'] = self.meta_forms
|
ctx['meta_forms'] = self.meta_forms
|
||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
self.object = None
|
||||||
|
form = self.get_form()
|
||||||
|
if form.is_valid() and all([f.is_valid() for f in self.meta_forms]):
|
||||||
|
return self.form_valid(form)
|
||||||
|
else:
|
||||||
|
return self.form_invalid(form)
|
||||||
|
|
||||||
|
|
||||||
class ItemUpdateGeneral(ItemDetailMixin, EventPermissionRequiredMixin, MetaDataEditorMixin, UpdateView):
|
class ItemUpdateGeneral(ItemDetailMixin, EventPermissionRequiredMixin, MetaDataEditorMixin, UpdateView):
|
||||||
form_class = ItemUpdateForm
|
form_class = ItemUpdateForm
|
||||||
@@ -1293,6 +1322,11 @@ class ItemUpdateGeneral(ItemDetailMixin, EventPermissionRequiredMixin, MetaDataE
|
|||||||
forms.extend(resp)
|
forms.extend(resp)
|
||||||
else:
|
else:
|
||||||
forms.append(resp)
|
forms.append(resp)
|
||||||
|
|
||||||
|
for form in forms:
|
||||||
|
if has_truthy_attr(form, "title") and has_truthy_attr(form, "is_layout"):
|
||||||
|
raise ValueError("`title` and `is_layout` must not both be truthy values")
|
||||||
|
|
||||||
return forms
|
return forms
|
||||||
|
|
||||||
def get_success_url(self) -> str:
|
def get_success_url(self) -> str:
|
||||||
|
|||||||
@@ -192,8 +192,8 @@ class MailSettingsSetupView(TemplateView):
|
|||||||
spf_record = get_spf_record(hostname)
|
spf_record = get_spf_record(hostname)
|
||||||
if not spf_record:
|
if not spf_record:
|
||||||
spf_warning = _(
|
spf_warning = _(
|
||||||
'We could not find an SPF record set for the domain you are trying to use. You can still '
|
'We could not find an SPF record set for the domain you are trying to use. This means that '
|
||||||
'proceed, but it will increase the chance of emails going to spam or being rejected. We '
|
'there is a very high change most of the emails will be rejected or marked as spam. We '
|
||||||
'strongly recommend setting an SPF record on the domain. You can do so through the DNS '
|
'strongly recommend setting an SPF record on the domain. You can do so through the DNS '
|
||||||
'settings at the provider you registered your domain with.'
|
'settings at the provider you registered your domain with.'
|
||||||
)
|
)
|
||||||
@@ -205,7 +205,8 @@ class MailSettingsSetupView(TemplateView):
|
|||||||
'this system in the SPF record.'
|
'this system in the SPF record.'
|
||||||
)
|
)
|
||||||
|
|
||||||
if settings.MAIL_CUSTOM_SENDER_VERIFICATION_REQUIRED:
|
verification = settings.MAIL_CUSTOM_SENDER_VERIFICATION_REQUIRED and not spf_warning
|
||||||
|
if verification:
|
||||||
if 'verification' in self.request.POST:
|
if 'verification' in self.request.POST:
|
||||||
messages.error(request, _('The verification code was incorrect, please try again.'))
|
messages.error(request, _('The verification code was incorrect, please try again.'))
|
||||||
else:
|
else:
|
||||||
@@ -230,7 +231,7 @@ class MailSettingsSetupView(TemplateView):
|
|||||||
context={
|
context={
|
||||||
'basetpl': self.basetpl,
|
'basetpl': self.basetpl,
|
||||||
'object': self.object,
|
'object': self.object,
|
||||||
'verification': settings.MAIL_CUSTOM_SENDER_VERIFICATION_REQUIRED,
|
'verification': verification,
|
||||||
'spf_warning': spf_warning,
|
'spf_warning': spf_warning,
|
||||||
'spf_record': spf_record,
|
'spf_record': spf_record,
|
||||||
'spf_key': settings.MAIL_CUSTOM_SENDER_SPF_STRING,
|
'spf_key': settings.MAIL_CUSTOM_SENDER_SPF_STRING,
|
||||||
|
|||||||
@@ -418,7 +418,7 @@ class OrderTransactions(OrderView):
|
|||||||
'item', 'variation', 'subevent'
|
'item', 'variation', 'subevent'
|
||||||
).order_by('datetime')
|
).order_by('datetime')
|
||||||
ctx['sums'] = self.order.transactions.aggregate(
|
ctx['sums'] = self.order.transactions.aggregate(
|
||||||
count=Sum('count'),
|
sum_count=Sum('count'),
|
||||||
full_price=Sum(F('count') * F('price')),
|
full_price=Sum(F('count') * F('price')),
|
||||||
full_tax_value=Sum(F('count') * F('tax_value')),
|
full_tax_value=Sum(F('count') * F('tax_value')),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -546,7 +546,7 @@ def variations_select2(request, **kwargs):
|
|||||||
F('item__category__position').asc(nulls_first=True),
|
F('item__category__position').asc(nulls_first=True),
|
||||||
'item__category_id',
|
'item__category_id',
|
||||||
'item__position',
|
'item__position',
|
||||||
'item__pk'
|
'item__pk',
|
||||||
'position',
|
'position',
|
||||||
'value'
|
'value'
|
||||||
).select_related('item')
|
).select_related('item')
|
||||||
@@ -718,7 +718,7 @@ def itemvarquota_select2(request, **kwargs):
|
|||||||
itemqs = request.event.items.prefetch_related('variations').filter(
|
itemqs = request.event.items.prefetch_related('variations').filter(
|
||||||
Q(name__icontains=i18ncomp(query)) | Q(internal_name__icontains=query)
|
Q(name__icontains=i18ncomp(query)) | Q(internal_name__icontains=query)
|
||||||
)
|
)
|
||||||
quotaqs = request.event.quotas.filter(quotaf).select_related('subevent')
|
quotaqs = request.event.quotas.filter(quotaf).select_related('subevent').order_by('-subevent__date_from', 'name')
|
||||||
more = False
|
more = False
|
||||||
else:
|
else:
|
||||||
if page == 1:
|
if page == 1:
|
||||||
@@ -727,7 +727,7 @@ def itemvarquota_select2(request, **kwargs):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
itemqs = request.event.items.none()
|
itemqs = request.event.items.none()
|
||||||
quotaqs = request.event.quotas.filter(name__icontains=query).select_related('subevent')
|
quotaqs = request.event.quotas.filter(name__icontains=query).select_related('subevent').order_by('-subevent__date_from', 'name')
|
||||||
total = quotaqs.count()
|
total = quotaqs.count()
|
||||||
pagesize = 20
|
pagesize = 20
|
||||||
offset = (page - 1) * pagesize
|
offset = (page - 1) * pagesize
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
# License for the specific language governing permissions and limitations under the License.
|
# License for the specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
import io
|
import io
|
||||||
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
import bleach
|
import bleach
|
||||||
from defusedcsv import csv
|
from defusedcsv import csv
|
||||||
@@ -75,6 +76,7 @@ from pretix.control.views import PaginationMixin
|
|||||||
from pretix.helpers.compat import CompatDeleteView
|
from pretix.helpers.compat import CompatDeleteView
|
||||||
from pretix.helpers.format import format_map
|
from pretix.helpers.format import format_map
|
||||||
from pretix.helpers.models import modelcopy
|
from pretix.helpers.models import modelcopy
|
||||||
|
from pretix.multidomain.urlreverse import build_absolute_uri
|
||||||
|
|
||||||
|
|
||||||
class VoucherList(PaginationMixin, EventPermissionRequiredMixin, ListView):
|
class VoucherList(PaginationMixin, EventPermissionRequiredMixin, ListView):
|
||||||
@@ -315,6 +317,13 @@ class VoucherUpdate(EventPermissionRequiredMixin, UpdateView):
|
|||||||
expires__gte=now()
|
expires__gte=now()
|
||||||
).count()
|
).count()
|
||||||
ctx['redeemed_in_carts'] = redeemed_in_carts
|
ctx['redeemed_in_carts'] = redeemed_in_carts
|
||||||
|
|
||||||
|
url_params = {
|
||||||
|
'voucher': self.object.code
|
||||||
|
}
|
||||||
|
if self.object.subevent_id:
|
||||||
|
url_params['subevent'] = self.object.subevent_id
|
||||||
|
ctx['url'] = build_absolute_uri(self.request.event, "presale:event.redeem") + "?" + urlencode(url_params)
|
||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
|
|
||||||
@@ -569,6 +578,8 @@ class VoucherRNG(EventPermissionRequiredMixin, View):
|
|||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
try:
|
try:
|
||||||
num = int(request.GET.get('num', '5'))
|
num = int(request.GET.get('num', '5'))
|
||||||
|
if num > 100_000:
|
||||||
|
return HttpResponseBadRequest()
|
||||||
except ValueError: # NOQA
|
except ValueError: # NOQA
|
||||||
return HttpResponseBadRequest()
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ class WaitingListQuerySetMixin:
|
|||||||
return self.request.POST
|
return self.request.POST
|
||||||
return self.request.GET
|
return self.request.GET
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self, force_filtered=False):
|
||||||
qs = WaitingListEntry.objects.filter(
|
qs = WaitingListEntry.objects.filter(
|
||||||
event=self.request.event
|
event=self.request.event
|
||||||
).select_related('item', 'variation', 'voucher').prefetch_related(
|
).select_related('item', 'variation', 'voucher').prefetch_related(
|
||||||
@@ -135,6 +135,8 @@ class WaitingListQuerySetMixin:
|
|||||||
qs = qs.filter(
|
qs = qs.filter(
|
||||||
id__in=self.request_data.getlist('entry')
|
id__in=self.request_data.getlist('entry')
|
||||||
)
|
)
|
||||||
|
elif force_filtered and '__ALL' not in self.request_data:
|
||||||
|
qs = qs.none()
|
||||||
|
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
@@ -158,7 +160,7 @@ class WaitingListActionView(EventPermissionRequiredMixin, WaitingListQuerySetMix
|
|||||||
'forbidden': self.get_queryset().filter(voucher__isnull=False),
|
'forbidden': self.get_queryset().filter(voucher__isnull=False),
|
||||||
})
|
})
|
||||||
elif request.POST.get('action') == 'delete_confirm':
|
elif request.POST.get('action') == 'delete_confirm':
|
||||||
for obj in self.get_queryset():
|
for obj in self.get_queryset(force_filtered=True):
|
||||||
if not obj.voucher_id:
|
if not obj.voucher_id:
|
||||||
obj.log_action('pretix.event.orders.waitinglist.deleted', user=self.request.user)
|
obj.log_action('pretix.event.orders.waitinglist.deleted', user=self.request.user)
|
||||||
obj.delete()
|
obj.delete()
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
+1836
-1528
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2023-06-27 12:51+0000\n"
|
"POT-Creation-Date: 2023-07-21 11:46+0000\n"
|
||||||
"PO-Revision-Date: 2021-09-15 11:22+0000\n"
|
"PO-Revision-Date: 2021-09-15 11:22+0000\n"
|
||||||
"Last-Translator: Mohamed Tawfiq <mtawfiq@wafyapp.com>\n"
|
"Last-Translator: Mohamed Tawfiq <mtawfiq@wafyapp.com>\n"
|
||||||
"Language-Team: Arabic <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
"Language-Team: Arabic <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||||
@@ -39,6 +39,7 @@ msgid "Venmo"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
||||||
|
#: pretix/static/pretixpresale/js/walletdetection.js:38
|
||||||
msgid "Apple Pay"
|
msgid "Apple Pay"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -138,8 +139,8 @@ msgid "Continue"
|
|||||||
msgstr "المتابعة"
|
msgstr "المتابعة"
|
||||||
|
|
||||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:157
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:204
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:188
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:235
|
||||||
msgid "Confirming your payment …"
|
msgid "Confirming your payment …"
|
||||||
msgstr "جاري تأكيد الدفع الخاص بك …"
|
msgstr "جاري تأكيد الدفع الخاص بك …"
|
||||||
|
|
||||||
@@ -161,15 +162,15 @@ msgstr "الطلبات المدفوعة"
|
|||||||
msgid "Total revenue"
|
msgid "Total revenue"
|
||||||
msgstr "إجمالي الإيرادات"
|
msgstr "إجمالي الإيرادات"
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:12
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:13
|
||||||
msgid "Contacting Stripe …"
|
msgid "Contacting Stripe …"
|
||||||
msgstr "جاري الاتصال بStripe …"
|
msgstr "جاري الاتصال بStripe …"
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:65
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:70
|
||||||
msgid "Total"
|
msgid "Total"
|
||||||
msgstr "المجموع"
|
msgstr "المجموع"
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:164
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:211
|
||||||
msgid "Contacting your bank …"
|
msgid "Contacting your bank …"
|
||||||
msgstr "جاري الاتصال بالبنك الذي تتعامل معه …"
|
msgstr "جاري الاتصال بالبنك الذي تتعامل معه …"
|
||||||
|
|
||||||
@@ -559,46 +560,46 @@ msgstr "توليد الرسائل …"
|
|||||||
msgid "Unknown error."
|
msgid "Unknown error."
|
||||||
msgstr "خطأ غير معروف."
|
msgstr "خطأ غير معروف."
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:309
|
#: pretix/static/pretixcontrol/js/ui/main.js:311
|
||||||
msgid "Your color has great contrast and is very easy to read!"
|
msgid "Your color has great contrast and is very easy to read!"
|
||||||
msgstr "اللون يتمتع بتباين كبير وتسهل قراءته!"
|
msgstr "اللون يتمتع بتباين كبير وتسهل قراءته!"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:313
|
#: pretix/static/pretixcontrol/js/ui/main.js:315
|
||||||
msgid "Your color has decent contrast and is probably good-enough to read!"
|
msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||||
msgstr "اللون يحظى بتباين معقول ويمكن أن يكون مناسب للقراءة!"
|
msgstr "اللون يحظى بتباين معقول ويمكن أن يكون مناسب للقراءة!"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:317
|
#: pretix/static/pretixcontrol/js/ui/main.js:319
|
||||||
msgid ""
|
msgid ""
|
||||||
"Your color has bad contrast for text on white background, please choose a "
|
"Your color has bad contrast for text on white background, please choose a "
|
||||||
"darker shade."
|
"darker shade."
|
||||||
msgstr "تباين اللون سيئ للخلفية البيضاء، الرجاء اختيار لون غامق."
|
msgstr "تباين اللون سيئ للخلفية البيضاء، الرجاء اختيار لون غامق."
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:466
|
#: pretix/static/pretixcontrol/js/ui/main.js:468
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
#: pretix/static/pretixcontrol/js/ui/main.js:488
|
||||||
msgid "Search query"
|
msgid "Search query"
|
||||||
msgstr "البحث في الاستفسارات"
|
msgstr "البحث في الاستفسارات"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:484
|
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr "الكل"
|
msgstr "الكل"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:485
|
#: pretix/static/pretixcontrol/js/ui/main.js:487
|
||||||
msgid "None"
|
msgid "None"
|
||||||
msgstr "لا شيء"
|
msgstr "لا شيء"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:489
|
#: pretix/static/pretixcontrol/js/ui/main.js:491
|
||||||
msgid "Selected only"
|
msgid "Selected only"
|
||||||
msgstr "المختارة فقط"
|
msgstr "المختارة فقط"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:892
|
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||||
msgid "Use a different name internally"
|
msgid "Use a different name internally"
|
||||||
msgstr "قم باستخدم اسم مختلف داخليا"
|
msgstr "قم باستخدم اسم مختلف داخليا"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:932
|
#: pretix/static/pretixcontrol/js/ui/main.js:934
|
||||||
msgid "Click to close"
|
msgid "Click to close"
|
||||||
msgstr "اضغط لاغلاق الصفحة"
|
msgstr "اضغط لاغلاق الصفحة"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:1007
|
#: pretix/static/pretixcontrol/js/ui/main.js:1009
|
||||||
msgid "You have unsaved changes!"
|
msgid "You have unsaved changes!"
|
||||||
msgstr "لم تقم بحفظ التعديلات!"
|
msgstr "لم تقم بحفظ التعديلات!"
|
||||||
|
|
||||||
@@ -672,23 +673,27 @@ msgstr "ستسترد %(currency)%(amount)"
|
|||||||
msgid "Please enter the amount the organizer can keep."
|
msgid "Please enter the amount the organizer can keep."
|
||||||
msgstr "الرجاء إدخال المبلغ الذي يمكن للمنظم الاحتفاظ به."
|
msgstr "الرجاء إدخال المبلغ الذي يمكن للمنظم الاحتفاظ به."
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:436
|
#: pretix/static/pretixpresale/js/ui/main.js:412
|
||||||
msgid "Please enter a quantity for one of the ticket types."
|
msgid "Please enter a quantity for one of the ticket types."
|
||||||
msgstr "الرجاء إدخال عدد لأحد أنواع التذاكر."
|
msgstr "الرجاء إدخال عدد لأحد أنواع التذاكر."
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:472
|
#: pretix/static/pretixpresale/js/ui/main.js:448
|
||||||
msgid "required"
|
msgid "required"
|
||||||
msgstr "مطلوب"
|
msgstr "مطلوب"
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:575
|
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:594
|
#: pretix/static/pretixpresale/js/ui/main.js:570
|
||||||
msgid "Time zone:"
|
msgid "Time zone:"
|
||||||
msgstr "المنطقة الزمنية:"
|
msgstr "المنطقة الزمنية:"
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:585
|
#: pretix/static/pretixpresale/js/ui/main.js:561
|
||||||
msgid "Your local time:"
|
msgid "Your local time:"
|
||||||
msgstr "التوقيت المحلي:"
|
msgstr "التوقيت المحلي:"
|
||||||
|
|
||||||
|
#: pretix/static/pretixpresale/js/walletdetection.js:39
|
||||||
|
msgid "Google Pay"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Quantity"
|
msgid "Quantity"
|
||||||
|
|||||||
+1838
-1547
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2023-06-27 12:51+0000\n"
|
"POT-Creation-Date: 2023-07-21 11:46+0000\n"
|
||||||
"PO-Revision-Date: 2020-12-19 07:00+0000\n"
|
"PO-Revision-Date: 2020-12-19 07:00+0000\n"
|
||||||
"Last-Translator: albert <albert.serra.monner@gmail.com>\n"
|
"Last-Translator: albert <albert.serra.monner@gmail.com>\n"
|
||||||
"Language-Team: Catalan <https://translate.pretix.eu/projects/pretix/pretix-"
|
"Language-Team: Catalan <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||||
@@ -38,6 +38,7 @@ msgid "Venmo"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
||||||
|
#: pretix/static/pretixpresale/js/walletdetection.js:38
|
||||||
msgid "Apple Pay"
|
msgid "Apple Pay"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -135,8 +136,8 @@ msgid "Continue"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:157
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:204
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:188
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:235
|
||||||
msgid "Confirming your payment …"
|
msgid "Confirming your payment …"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -158,15 +159,15 @@ msgstr ""
|
|||||||
msgid "Total revenue"
|
msgid "Total revenue"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:12
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:13
|
||||||
msgid "Contacting Stripe …"
|
msgid "Contacting Stripe …"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:65
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:70
|
||||||
msgid "Total"
|
msgid "Total"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:164
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:211
|
||||||
msgid "Contacting your bank …"
|
msgid "Contacting your bank …"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -538,46 +539,46 @@ msgstr ""
|
|||||||
msgid "Unknown error."
|
msgid "Unknown error."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:309
|
#: pretix/static/pretixcontrol/js/ui/main.js:311
|
||||||
msgid "Your color has great contrast and is very easy to read!"
|
msgid "Your color has great contrast and is very easy to read!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:313
|
#: pretix/static/pretixcontrol/js/ui/main.js:315
|
||||||
msgid "Your color has decent contrast and is probably good-enough to read!"
|
msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:317
|
#: pretix/static/pretixcontrol/js/ui/main.js:319
|
||||||
msgid ""
|
msgid ""
|
||||||
"Your color has bad contrast for text on white background, please choose a "
|
"Your color has bad contrast for text on white background, please choose a "
|
||||||
"darker shade."
|
"darker shade."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:466
|
#: pretix/static/pretixcontrol/js/ui/main.js:468
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
#: pretix/static/pretixcontrol/js/ui/main.js:488
|
||||||
msgid "Search query"
|
msgid "Search query"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:484
|
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:485
|
#: pretix/static/pretixcontrol/js/ui/main.js:487
|
||||||
msgid "None"
|
msgid "None"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:489
|
#: pretix/static/pretixcontrol/js/ui/main.js:491
|
||||||
msgid "Selected only"
|
msgid "Selected only"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:892
|
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||||
msgid "Use a different name internally"
|
msgid "Use a different name internally"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:932
|
#: pretix/static/pretixcontrol/js/ui/main.js:934
|
||||||
msgid "Click to close"
|
msgid "Click to close"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:1007
|
#: pretix/static/pretixcontrol/js/ui/main.js:1009
|
||||||
msgid "You have unsaved changes!"
|
msgid "You have unsaved changes!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -641,25 +642,29 @@ msgstr ""
|
|||||||
msgid "Please enter the amount the organizer can keep."
|
msgid "Please enter the amount the organizer can keep."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:436
|
#: pretix/static/pretixpresale/js/ui/main.js:412
|
||||||
msgid "Please enter a quantity for one of the ticket types."
|
msgid "Please enter a quantity for one of the ticket types."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:472
|
#: pretix/static/pretixpresale/js/ui/main.js:448
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
#| msgid "Cart expired"
|
#| msgid "Cart expired"
|
||||||
msgid "required"
|
msgid "required"
|
||||||
msgstr "Cistella expirada"
|
msgstr "Cistella expirada"
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:575
|
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:594
|
#: pretix/static/pretixpresale/js/ui/main.js:570
|
||||||
msgid "Time zone:"
|
msgid "Time zone:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:585
|
#: pretix/static/pretixpresale/js/ui/main.js:561
|
||||||
msgid "Your local time:"
|
msgid "Your local time:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixpresale/js/walletdetection.js:39
|
||||||
|
msgid "Google Pay"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Quantity"
|
msgid "Quantity"
|
||||||
|
|||||||
+1840
-1528
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2023-06-27 12:51+0000\n"
|
"POT-Creation-Date: 2023-07-21 11:46+0000\n"
|
||||||
"PO-Revision-Date: 2023-03-19 06:00+0000\n"
|
"PO-Revision-Date: 2023-03-19 06:00+0000\n"
|
||||||
"Last-Translator: Michael <michael.happl@gmx.at>\n"
|
"Last-Translator: Michael <michael.happl@gmx.at>\n"
|
||||||
"Language-Team: Czech <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
"Language-Team: Czech <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||||
@@ -38,6 +38,7 @@ msgid "Venmo"
|
|||||||
msgstr "Venmo"
|
msgstr "Venmo"
|
||||||
|
|
||||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
||||||
|
#: pretix/static/pretixpresale/js/walletdetection.js:38
|
||||||
msgid "Apple Pay"
|
msgid "Apple Pay"
|
||||||
msgstr "Apple Pay"
|
msgstr "Apple Pay"
|
||||||
|
|
||||||
@@ -135,8 +136,8 @@ msgid "Continue"
|
|||||||
msgstr "Pokračovat"
|
msgstr "Pokračovat"
|
||||||
|
|
||||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:157
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:204
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:188
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:235
|
||||||
msgid "Confirming your payment …"
|
msgid "Confirming your payment …"
|
||||||
msgstr "Potvrzuji vaši platbu …"
|
msgstr "Potvrzuji vaši platbu …"
|
||||||
|
|
||||||
@@ -158,15 +159,15 @@ msgstr "Zaplacené objednávky"
|
|||||||
msgid "Total revenue"
|
msgid "Total revenue"
|
||||||
msgstr "Celkové příjmy"
|
msgstr "Celkové příjmy"
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:12
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:13
|
||||||
msgid "Contacting Stripe …"
|
msgid "Contacting Stripe …"
|
||||||
msgstr "Kontaktuji Stripe …"
|
msgstr "Kontaktuji Stripe …"
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:65
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:70
|
||||||
msgid "Total"
|
msgid "Total"
|
||||||
msgstr "Celkem"
|
msgstr "Celkem"
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:164
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:211
|
||||||
msgid "Contacting your bank …"
|
msgid "Contacting your bank …"
|
||||||
msgstr "Kontaktuji vaši banku …"
|
msgstr "Kontaktuji vaši banku …"
|
||||||
|
|
||||||
@@ -550,16 +551,16 @@ msgstr "Vytváření zpráv…"
|
|||||||
msgid "Unknown error."
|
msgid "Unknown error."
|
||||||
msgstr "Neznámá chyba."
|
msgstr "Neznámá chyba."
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:309
|
#: pretix/static/pretixcontrol/js/ui/main.js:311
|
||||||
msgid "Your color has great contrast and is very easy to read!"
|
msgid "Your color has great contrast and is very easy to read!"
|
||||||
msgstr "Tato barva má velmi dobrý kontrast a je velmi dobře čitelná!"
|
msgstr "Tato barva má velmi dobrý kontrast a je velmi dobře čitelná!"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:313
|
#: pretix/static/pretixcontrol/js/ui/main.js:315
|
||||||
msgid "Your color has decent contrast and is probably good-enough to read!"
|
msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Tato barva má slušný kontrast a pravděpodobně je dostatečně dobře čitelná!"
|
"Tato barva má slušný kontrast a pravděpodobně je dostatečně dobře čitelná!"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:317
|
#: pretix/static/pretixcontrol/js/ui/main.js:319
|
||||||
msgid ""
|
msgid ""
|
||||||
"Your color has bad contrast for text on white background, please choose a "
|
"Your color has bad contrast for text on white background, please choose a "
|
||||||
"darker shade."
|
"darker shade."
|
||||||
@@ -567,32 +568,32 @@ msgstr ""
|
|||||||
"Tato barva je pro text na bílém pozadí špatně kontrastní, zvolte prosím "
|
"Tato barva je pro text na bílém pozadí špatně kontrastní, zvolte prosím "
|
||||||
"tmavší odstín."
|
"tmavší odstín."
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:466
|
#: pretix/static/pretixcontrol/js/ui/main.js:468
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
#: pretix/static/pretixcontrol/js/ui/main.js:488
|
||||||
msgid "Search query"
|
msgid "Search query"
|
||||||
msgstr "Hledaný výraz"
|
msgstr "Hledaný výraz"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:484
|
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr "Všechny"
|
msgstr "Všechny"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:485
|
#: pretix/static/pretixcontrol/js/ui/main.js:487
|
||||||
msgid "None"
|
msgid "None"
|
||||||
msgstr "Žádný"
|
msgstr "Žádný"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:489
|
#: pretix/static/pretixcontrol/js/ui/main.js:491
|
||||||
msgid "Selected only"
|
msgid "Selected only"
|
||||||
msgstr "Pouze vybrané"
|
msgstr "Pouze vybrané"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:892
|
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||||
msgid "Use a different name internally"
|
msgid "Use a different name internally"
|
||||||
msgstr "Interně používat jiný název"
|
msgstr "Interně používat jiný název"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:932
|
#: pretix/static/pretixcontrol/js/ui/main.js:934
|
||||||
msgid "Click to close"
|
msgid "Click to close"
|
||||||
msgstr "Kliknutím zavřete"
|
msgstr "Kliknutím zavřete"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:1007
|
#: pretix/static/pretixcontrol/js/ui/main.js:1009
|
||||||
msgid "You have unsaved changes!"
|
msgid "You have unsaved changes!"
|
||||||
msgstr "Máte neuložené změny!"
|
msgstr "Máte neuložené změny!"
|
||||||
|
|
||||||
@@ -659,23 +660,29 @@ msgstr "Dostanete %(currency)s %(amount)s zpět"
|
|||||||
msgid "Please enter the amount the organizer can keep."
|
msgid "Please enter the amount the organizer can keep."
|
||||||
msgstr "Zadejte částku, kterou si organizátor může ponechat."
|
msgstr "Zadejte částku, kterou si organizátor může ponechat."
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:436
|
#: pretix/static/pretixpresale/js/ui/main.js:412
|
||||||
msgid "Please enter a quantity for one of the ticket types."
|
msgid "Please enter a quantity for one of the ticket types."
|
||||||
msgstr "Zadejte prosím množství pro jeden z typů vstupenek."
|
msgstr "Zadejte prosím množství pro jeden z typů vstupenek."
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:472
|
#: pretix/static/pretixpresale/js/ui/main.js:448
|
||||||
msgid "required"
|
msgid "required"
|
||||||
msgstr "povinný"
|
msgstr "povinný"
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:575
|
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:594
|
#: pretix/static/pretixpresale/js/ui/main.js:570
|
||||||
msgid "Time zone:"
|
msgid "Time zone:"
|
||||||
msgstr "Časové pásmo:"
|
msgstr "Časové pásmo:"
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:585
|
#: pretix/static/pretixpresale/js/ui/main.js:561
|
||||||
msgid "Your local time:"
|
msgid "Your local time:"
|
||||||
msgstr "Místní čas:"
|
msgstr "Místní čas:"
|
||||||
|
|
||||||
|
#: pretix/static/pretixpresale/js/walletdetection.js:39
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Apple Pay"
|
||||||
|
msgid "Google Pay"
|
||||||
|
msgstr "Apple Pay"
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Quantity"
|
msgid "Quantity"
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
+1815
-1529
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: \n"
|
"Project-Id-Version: \n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2023-06-27 12:51+0000\n"
|
"POT-Creation-Date: 2023-07-21 11:46+0000\n"
|
||||||
"PO-Revision-Date: 2022-12-01 17:00+0000\n"
|
"PO-Revision-Date: 2022-12-01 17:00+0000\n"
|
||||||
"Last-Translator: Mie Frydensbjerg <mif@aarhus.dk>\n"
|
"Last-Translator: Mie Frydensbjerg <mif@aarhus.dk>\n"
|
||||||
"Language-Team: Danish <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
"Language-Team: Danish <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||||
@@ -37,6 +37,7 @@ msgid "Venmo"
|
|||||||
msgstr "Venmo"
|
msgstr "Venmo"
|
||||||
|
|
||||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
||||||
|
#: pretix/static/pretixpresale/js/walletdetection.js:38
|
||||||
msgid "Apple Pay"
|
msgid "Apple Pay"
|
||||||
msgstr "Apple Pay"
|
msgstr "Apple Pay"
|
||||||
|
|
||||||
@@ -139,8 +140,8 @@ msgid "Continue"
|
|||||||
msgstr "Fortsæt"
|
msgstr "Fortsæt"
|
||||||
|
|
||||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:157
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:204
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:188
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:235
|
||||||
msgid "Confirming your payment …"
|
msgid "Confirming your payment …"
|
||||||
msgstr "Bekræfter din betaling …"
|
msgstr "Bekræfter din betaling …"
|
||||||
|
|
||||||
@@ -162,15 +163,15 @@ msgstr "Betalte bestillinger"
|
|||||||
msgid "Total revenue"
|
msgid "Total revenue"
|
||||||
msgstr "Omsætning i alt"
|
msgstr "Omsætning i alt"
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:12
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:13
|
||||||
msgid "Contacting Stripe …"
|
msgid "Contacting Stripe …"
|
||||||
msgstr "Kontakter Stripe …"
|
msgstr "Kontakter Stripe …"
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:65
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:70
|
||||||
msgid "Total"
|
msgid "Total"
|
||||||
msgstr "Total"
|
msgstr "Total"
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:164
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:211
|
||||||
msgid "Contacting your bank …"
|
msgid "Contacting your bank …"
|
||||||
msgstr "Kontakter din bank …"
|
msgstr "Kontakter din bank …"
|
||||||
|
|
||||||
@@ -573,46 +574,46 @@ msgstr "Opretter beskeder …"
|
|||||||
msgid "Unknown error."
|
msgid "Unknown error."
|
||||||
msgstr "Ukendt fejl."
|
msgstr "Ukendt fejl."
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:309
|
#: pretix/static/pretixcontrol/js/ui/main.js:311
|
||||||
msgid "Your color has great contrast and is very easy to read!"
|
msgid "Your color has great contrast and is very easy to read!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:313
|
#: pretix/static/pretixcontrol/js/ui/main.js:315
|
||||||
msgid "Your color has decent contrast and is probably good-enough to read!"
|
msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:317
|
#: pretix/static/pretixcontrol/js/ui/main.js:319
|
||||||
msgid ""
|
msgid ""
|
||||||
"Your color has bad contrast for text on white background, please choose a "
|
"Your color has bad contrast for text on white background, please choose a "
|
||||||
"darker shade."
|
"darker shade."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:466
|
#: pretix/static/pretixcontrol/js/ui/main.js:468
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
#: pretix/static/pretixcontrol/js/ui/main.js:488
|
||||||
msgid "Search query"
|
msgid "Search query"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:484
|
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr "Alle"
|
msgstr "Alle"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:485
|
#: pretix/static/pretixcontrol/js/ui/main.js:487
|
||||||
msgid "None"
|
msgid "None"
|
||||||
msgstr "Ingen"
|
msgstr "Ingen"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:489
|
#: pretix/static/pretixcontrol/js/ui/main.js:491
|
||||||
msgid "Selected only"
|
msgid "Selected only"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:892
|
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||||
msgid "Use a different name internally"
|
msgid "Use a different name internally"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:932
|
#: pretix/static/pretixcontrol/js/ui/main.js:934
|
||||||
msgid "Click to close"
|
msgid "Click to close"
|
||||||
msgstr "Klik for at lukke"
|
msgstr "Klik for at lukke"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:1007
|
#: pretix/static/pretixcontrol/js/ui/main.js:1009
|
||||||
msgid "You have unsaved changes!"
|
msgid "You have unsaved changes!"
|
||||||
msgstr "Du har ændringer, der ikke er gemt!"
|
msgstr "Du har ændringer, der ikke er gemt!"
|
||||||
|
|
||||||
@@ -686,25 +687,31 @@ msgstr "fra %(currency)s %(price)s"
|
|||||||
msgid "Please enter the amount the organizer can keep."
|
msgid "Please enter the amount the organizer can keep."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:436
|
#: pretix/static/pretixpresale/js/ui/main.js:412
|
||||||
msgid "Please enter a quantity for one of the ticket types."
|
msgid "Please enter a quantity for one of the ticket types."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:472
|
#: pretix/static/pretixpresale/js/ui/main.js:448
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
#| msgid "Cart expired"
|
#| msgid "Cart expired"
|
||||||
msgid "required"
|
msgid "required"
|
||||||
msgstr "Kurv udløbet"
|
msgstr "Kurv udløbet"
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:575
|
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:594
|
#: pretix/static/pretixpresale/js/ui/main.js:570
|
||||||
msgid "Time zone:"
|
msgid "Time zone:"
|
||||||
msgstr "Tidszone:"
|
msgstr "Tidszone:"
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:585
|
#: pretix/static/pretixpresale/js/ui/main.js:561
|
||||||
msgid "Your local time:"
|
msgid "Your local time:"
|
||||||
msgstr "Din lokaltid:"
|
msgstr "Din lokaltid:"
|
||||||
|
|
||||||
|
#: pretix/static/pretixpresale/js/walletdetection.js:39
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Apple Pay"
|
||||||
|
msgid "Google Pay"
|
||||||
|
msgstr "Apple Pay"
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Quantity"
|
msgid "Quantity"
|
||||||
|
|||||||
+1821
-1546
File diff suppressed because it is too large
Load Diff
@@ -7,8 +7,8 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: \n"
|
"Project-Id-Version: \n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2023-06-27 12:51+0000\n"
|
"POT-Creation-Date: 2023-07-21 11:46+0000\n"
|
||||||
"PO-Revision-Date: 2023-06-27 14:53+0000\n"
|
"PO-Revision-Date: 2023-07-21 12:47+0000\n"
|
||||||
"Last-Translator: Raphael Michel <michel@rami.io>\n"
|
"Last-Translator: Raphael Michel <michel@rami.io>\n"
|
||||||
"Language-Team: German <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
"Language-Team: German <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||||
"de/>\n"
|
"de/>\n"
|
||||||
@@ -38,6 +38,7 @@ msgid "Venmo"
|
|||||||
msgstr "Venmo"
|
msgstr "Venmo"
|
||||||
|
|
||||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
||||||
|
#: pretix/static/pretixpresale/js/walletdetection.js:38
|
||||||
msgid "Apple Pay"
|
msgid "Apple Pay"
|
||||||
msgstr "Apple Pay"
|
msgstr "Apple Pay"
|
||||||
|
|
||||||
@@ -135,8 +136,8 @@ msgid "Continue"
|
|||||||
msgstr "Fortfahren"
|
msgstr "Fortfahren"
|
||||||
|
|
||||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:157
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:204
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:188
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:235
|
||||||
msgid "Confirming your payment …"
|
msgid "Confirming your payment …"
|
||||||
msgstr "Zahlung wird bestätigt …"
|
msgstr "Zahlung wird bestätigt …"
|
||||||
|
|
||||||
@@ -158,15 +159,15 @@ msgstr "Bezahlte Bestellungen"
|
|||||||
msgid "Total revenue"
|
msgid "Total revenue"
|
||||||
msgstr "Gesamtumsatz"
|
msgstr "Gesamtumsatz"
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:12
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:13
|
||||||
msgid "Contacting Stripe …"
|
msgid "Contacting Stripe …"
|
||||||
msgstr "Kontaktiere Stripe …"
|
msgstr "Kontaktiere Stripe …"
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:65
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:70
|
||||||
msgid "Total"
|
msgid "Total"
|
||||||
msgstr "Gesamt"
|
msgstr "Gesamt"
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:164
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:211
|
||||||
msgid "Contacting your bank …"
|
msgid "Contacting your bank …"
|
||||||
msgstr "Kontaktiere Ihre Bank …"
|
msgstr "Kontaktiere Ihre Bank …"
|
||||||
|
|
||||||
@@ -555,17 +556,17 @@ msgstr "Generiere Nachrichten…"
|
|||||||
msgid "Unknown error."
|
msgid "Unknown error."
|
||||||
msgstr "Unbekannter Fehler."
|
msgstr "Unbekannter Fehler."
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:309
|
#: pretix/static/pretixcontrol/js/ui/main.js:311
|
||||||
msgid "Your color has great contrast and is very easy to read!"
|
msgid "Your color has great contrast and is very easy to read!"
|
||||||
msgstr "Diese Farbe hat einen sehr guten Kontrast und ist sehr gut zu lesen!"
|
msgstr "Diese Farbe hat einen sehr guten Kontrast und ist sehr gut zu lesen!"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:313
|
#: pretix/static/pretixcontrol/js/ui/main.js:315
|
||||||
msgid "Your color has decent contrast and is probably good-enough to read!"
|
msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Diese Farbe hat einen ausreichenden Kontrast und ist wahrscheinlich gut zu "
|
"Diese Farbe hat einen ausreichenden Kontrast und ist wahrscheinlich gut zu "
|
||||||
"lesen!"
|
"lesen!"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:317
|
#: pretix/static/pretixcontrol/js/ui/main.js:319
|
||||||
msgid ""
|
msgid ""
|
||||||
"Your color has bad contrast for text on white background, please choose a "
|
"Your color has bad contrast for text on white background, please choose a "
|
||||||
"darker shade."
|
"darker shade."
|
||||||
@@ -573,32 +574,32 @@ msgstr ""
|
|||||||
"Diese Farbe hat einen schlechten Kontrast für Text auf einem weißen "
|
"Diese Farbe hat einen schlechten Kontrast für Text auf einem weißen "
|
||||||
"Hintergrund. Bitte wählen Sie eine dunklere Farbe."
|
"Hintergrund. Bitte wählen Sie eine dunklere Farbe."
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:466
|
#: pretix/static/pretixcontrol/js/ui/main.js:468
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
#: pretix/static/pretixcontrol/js/ui/main.js:488
|
||||||
msgid "Search query"
|
msgid "Search query"
|
||||||
msgstr "Suchbegriff"
|
msgstr "Suchbegriff"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:484
|
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr "Alle"
|
msgstr "Alle"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:485
|
#: pretix/static/pretixcontrol/js/ui/main.js:487
|
||||||
msgid "None"
|
msgid "None"
|
||||||
msgstr "Keine"
|
msgstr "Keine"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:489
|
#: pretix/static/pretixcontrol/js/ui/main.js:491
|
||||||
msgid "Selected only"
|
msgid "Selected only"
|
||||||
msgstr "Nur ausgewählte"
|
msgstr "Nur ausgewählte"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:892
|
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||||
msgid "Use a different name internally"
|
msgid "Use a different name internally"
|
||||||
msgstr "Intern einen anderen Namen verwenden"
|
msgstr "Intern einen anderen Namen verwenden"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:932
|
#: pretix/static/pretixcontrol/js/ui/main.js:934
|
||||||
msgid "Click to close"
|
msgid "Click to close"
|
||||||
msgstr "Klicken zum Schließen"
|
msgstr "Klicken zum Schließen"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:1007
|
#: pretix/static/pretixcontrol/js/ui/main.js:1009
|
||||||
msgid "You have unsaved changes!"
|
msgid "You have unsaved changes!"
|
||||||
msgstr "Sie haben ungespeicherte Änderungen!"
|
msgstr "Sie haben ungespeicherte Änderungen!"
|
||||||
|
|
||||||
@@ -663,23 +664,29 @@ msgstr "Sie erhalten %(currency)s %(amount)s zurück"
|
|||||||
msgid "Please enter the amount the organizer can keep."
|
msgid "Please enter the amount the organizer can keep."
|
||||||
msgstr "Bitte geben Sie den Betrag ein, den der Veranstalter einbehalten darf."
|
msgstr "Bitte geben Sie den Betrag ein, den der Veranstalter einbehalten darf."
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:436
|
#: pretix/static/pretixpresale/js/ui/main.js:412
|
||||||
msgid "Please enter a quantity for one of the ticket types."
|
msgid "Please enter a quantity for one of the ticket types."
|
||||||
msgstr "Bitte tragen Sie eine Menge für eines der Produkte ein."
|
msgstr "Bitte tragen Sie eine Menge für eines der Produkte ein."
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:472
|
#: pretix/static/pretixpresale/js/ui/main.js:448
|
||||||
msgid "required"
|
msgid "required"
|
||||||
msgstr "verpflichtend"
|
msgstr "verpflichtend"
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:575
|
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:594
|
#: pretix/static/pretixpresale/js/ui/main.js:570
|
||||||
msgid "Time zone:"
|
msgid "Time zone:"
|
||||||
msgstr "Zeitzone:"
|
msgstr "Zeitzone:"
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:585
|
#: pretix/static/pretixpresale/js/ui/main.js:561
|
||||||
msgid "Your local time:"
|
msgid "Your local time:"
|
||||||
msgstr "Deine lokale Zeit:"
|
msgstr "Deine lokale Zeit:"
|
||||||
|
|
||||||
|
#: pretix/static/pretixpresale/js/walletdetection.js:39
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Apple Pay"
|
||||||
|
msgid "Google Pay"
|
||||||
|
msgstr "Apple Pay"
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Quantity"
|
msgid "Quantity"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
2FA
|
2FA
|
||||||
ABN
|
ABN
|
||||||
|
AES
|
||||||
Absenderadresse
|
Absenderadresse
|
||||||
Absenderinformation
|
Absenderinformation
|
||||||
Absendername
|
Absendername
|
||||||
@@ -74,6 +75,7 @@ Cronjob
|
|||||||
csv
|
csv
|
||||||
Customer
|
Customer
|
||||||
CZK
|
CZK
|
||||||
|
Dashboard
|
||||||
Debug
|
Debug
|
||||||
dekodi
|
dekodi
|
||||||
deliverability
|
deliverability
|
||||||
@@ -178,6 +180,7 @@ Merchandise
|
|||||||
Meta
|
Meta
|
||||||
Metadaten
|
Metadaten
|
||||||
Mi
|
Mi
|
||||||
|
Mifare
|
||||||
Mitgliedschafts
|
Mitgliedschafts
|
||||||
Mitgliedschaftsdauer
|
Mitgliedschaftsdauer
|
||||||
min
|
min
|
||||||
@@ -191,6 +194,7 @@ NFC
|
|||||||
Nr
|
Nr
|
||||||
NREI
|
NREI
|
||||||
number
|
number
|
||||||
|
NXP
|
||||||
OK
|
OK
|
||||||
On
|
On
|
||||||
Open
|
Open
|
||||||
@@ -215,6 +219,7 @@ Plugins
|
|||||||
POS
|
POS
|
||||||
Postfix
|
Postfix
|
||||||
PostgreSQL
|
PostgreSQL
|
||||||
|
PPRO
|
||||||
prefix
|
prefix
|
||||||
pretix
|
pretix
|
||||||
pretixdesk
|
pretixdesk
|
||||||
@@ -313,6 +318,7 @@ txt
|
|||||||
überzahlte
|
überzahlte
|
||||||
uhrzeit
|
uhrzeit
|
||||||
UID
|
UID
|
||||||
|
Ultralight
|
||||||
ungespeicherte
|
ungespeicherte
|
||||||
unkategorisiert
|
unkategorisiert
|
||||||
Unkategorisierte
|
Unkategorisierte
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: \n"
|
"Project-Id-Version: \n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2023-06-27 12:51+0000\n"
|
"POT-Creation-Date: 2023-07-21 11:46+0000\n"
|
||||||
"PO-Revision-Date: 2023-06-27 14:35+0000\n"
|
"PO-Revision-Date: 2023-06-27 14:35+0000\n"
|
||||||
"Last-Translator: Raphael Michel <michel@rami.io>\n"
|
"Last-Translator: Raphael Michel <michel@rami.io>\n"
|
||||||
"Language-Team: German (informal) <https://translate.pretix.eu/projects/"
|
"Language-Team: German (informal) <https://translate.pretix.eu/projects/"
|
||||||
@@ -38,6 +38,7 @@ msgid "Venmo"
|
|||||||
msgstr "Venmo"
|
msgstr "Venmo"
|
||||||
|
|
||||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
||||||
|
#: pretix/static/pretixpresale/js/walletdetection.js:38
|
||||||
msgid "Apple Pay"
|
msgid "Apple Pay"
|
||||||
msgstr "Apple Pay"
|
msgstr "Apple Pay"
|
||||||
|
|
||||||
@@ -135,8 +136,8 @@ msgid "Continue"
|
|||||||
msgstr "Fortfahren"
|
msgstr "Fortfahren"
|
||||||
|
|
||||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:157
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:204
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:188
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:235
|
||||||
msgid "Confirming your payment …"
|
msgid "Confirming your payment …"
|
||||||
msgstr "Zahlung wird bestätigt …"
|
msgstr "Zahlung wird bestätigt …"
|
||||||
|
|
||||||
@@ -158,15 +159,15 @@ msgstr "Bezahlte Bestellungen"
|
|||||||
msgid "Total revenue"
|
msgid "Total revenue"
|
||||||
msgstr "Gesamtumsatz"
|
msgstr "Gesamtumsatz"
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:12
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:13
|
||||||
msgid "Contacting Stripe …"
|
msgid "Contacting Stripe …"
|
||||||
msgstr "Kontaktiere Stripe …"
|
msgstr "Kontaktiere Stripe …"
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:65
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:70
|
||||||
msgid "Total"
|
msgid "Total"
|
||||||
msgstr "Gesamt"
|
msgstr "Gesamt"
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:164
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:211
|
||||||
msgid "Contacting your bank …"
|
msgid "Contacting your bank …"
|
||||||
msgstr "Kontaktiere deine Bank …"
|
msgstr "Kontaktiere deine Bank …"
|
||||||
|
|
||||||
@@ -554,17 +555,17 @@ msgstr "Generiere Nachrichten…"
|
|||||||
msgid "Unknown error."
|
msgid "Unknown error."
|
||||||
msgstr "Unbekannter Fehler."
|
msgstr "Unbekannter Fehler."
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:309
|
#: pretix/static/pretixcontrol/js/ui/main.js:311
|
||||||
msgid "Your color has great contrast and is very easy to read!"
|
msgid "Your color has great contrast and is very easy to read!"
|
||||||
msgstr "Diese Farbe hat einen sehr guten Kontrast und ist sehr gut zu lesen!"
|
msgstr "Diese Farbe hat einen sehr guten Kontrast und ist sehr gut zu lesen!"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:313
|
#: pretix/static/pretixcontrol/js/ui/main.js:315
|
||||||
msgid "Your color has decent contrast and is probably good-enough to read!"
|
msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Diese Farbe hat einen ausreichenden Kontrast und ist wahrscheinlich gut zu "
|
"Diese Farbe hat einen ausreichenden Kontrast und ist wahrscheinlich gut zu "
|
||||||
"lesen!"
|
"lesen!"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:317
|
#: pretix/static/pretixcontrol/js/ui/main.js:319
|
||||||
msgid ""
|
msgid ""
|
||||||
"Your color has bad contrast for text on white background, please choose a "
|
"Your color has bad contrast for text on white background, please choose a "
|
||||||
"darker shade."
|
"darker shade."
|
||||||
@@ -572,32 +573,32 @@ msgstr ""
|
|||||||
"Diese Farbe hat einen schlechten Kontrast für Text auf einem weißen "
|
"Diese Farbe hat einen schlechten Kontrast für Text auf einem weißen "
|
||||||
"Hintergrund. Bitte wähle eine dunklere Farbe."
|
"Hintergrund. Bitte wähle eine dunklere Farbe."
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:466
|
#: pretix/static/pretixcontrol/js/ui/main.js:468
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
#: pretix/static/pretixcontrol/js/ui/main.js:488
|
||||||
msgid "Search query"
|
msgid "Search query"
|
||||||
msgstr "Suchbegriff"
|
msgstr "Suchbegriff"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:484
|
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr "Alle"
|
msgstr "Alle"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:485
|
#: pretix/static/pretixcontrol/js/ui/main.js:487
|
||||||
msgid "None"
|
msgid "None"
|
||||||
msgstr "Keine"
|
msgstr "Keine"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:489
|
#: pretix/static/pretixcontrol/js/ui/main.js:491
|
||||||
msgid "Selected only"
|
msgid "Selected only"
|
||||||
msgstr "Nur ausgewählte"
|
msgstr "Nur ausgewählte"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:892
|
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||||
msgid "Use a different name internally"
|
msgid "Use a different name internally"
|
||||||
msgstr "Intern einen anderen Namen verwenden"
|
msgstr "Intern einen anderen Namen verwenden"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:932
|
#: pretix/static/pretixcontrol/js/ui/main.js:934
|
||||||
msgid "Click to close"
|
msgid "Click to close"
|
||||||
msgstr "Klicken zum Schließen"
|
msgstr "Klicken zum Schließen"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:1007
|
#: pretix/static/pretixcontrol/js/ui/main.js:1009
|
||||||
msgid "You have unsaved changes!"
|
msgid "You have unsaved changes!"
|
||||||
msgstr "Du hast ungespeicherte Änderungen!"
|
msgstr "Du hast ungespeicherte Änderungen!"
|
||||||
|
|
||||||
@@ -662,23 +663,29 @@ msgstr "Du erhältst %(currency)s %(amount)s zurück"
|
|||||||
msgid "Please enter the amount the organizer can keep."
|
msgid "Please enter the amount the organizer can keep."
|
||||||
msgstr "Bitte gib den Betrag ein, den der Veranstalter einbehalten darf."
|
msgstr "Bitte gib den Betrag ein, den der Veranstalter einbehalten darf."
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:436
|
#: pretix/static/pretixpresale/js/ui/main.js:412
|
||||||
msgid "Please enter a quantity for one of the ticket types."
|
msgid "Please enter a quantity for one of the ticket types."
|
||||||
msgstr "Bitte trage eine Menge für eines der Produkte ein."
|
msgstr "Bitte trage eine Menge für eines der Produkte ein."
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:472
|
#: pretix/static/pretixpresale/js/ui/main.js:448
|
||||||
msgid "required"
|
msgid "required"
|
||||||
msgstr "verpflichtend"
|
msgstr "verpflichtend"
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:575
|
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:594
|
#: pretix/static/pretixpresale/js/ui/main.js:570
|
||||||
msgid "Time zone:"
|
msgid "Time zone:"
|
||||||
msgstr "Zeitzone:"
|
msgstr "Zeitzone:"
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:585
|
#: pretix/static/pretixpresale/js/ui/main.js:561
|
||||||
msgid "Your local time:"
|
msgid "Your local time:"
|
||||||
msgstr "Deine lokale Zeit:"
|
msgstr "Deine lokale Zeit:"
|
||||||
|
|
||||||
|
#: pretix/static/pretixpresale/js/walletdetection.js:39
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Apple Pay"
|
||||||
|
msgid "Google Pay"
|
||||||
|
msgstr "Apple Pay"
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Quantity"
|
msgid "Quantity"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
2FA
|
2FA
|
||||||
ABN
|
ABN
|
||||||
|
AES
|
||||||
Absenderadresse
|
Absenderadresse
|
||||||
Absenderinformation
|
Absenderinformation
|
||||||
Absendername
|
Absendername
|
||||||
@@ -74,6 +75,7 @@ Cronjob
|
|||||||
csv
|
csv
|
||||||
Customer
|
Customer
|
||||||
CZK
|
CZK
|
||||||
|
Dashboard
|
||||||
Debug
|
Debug
|
||||||
dekodi
|
dekodi
|
||||||
deliverability
|
deliverability
|
||||||
@@ -178,6 +180,7 @@ Merchandise
|
|||||||
Meta
|
Meta
|
||||||
Metadaten
|
Metadaten
|
||||||
Mi
|
Mi
|
||||||
|
Mifare
|
||||||
Mitgliedschafts
|
Mitgliedschafts
|
||||||
Mitgliedschaftsdauer
|
Mitgliedschaftsdauer
|
||||||
min
|
min
|
||||||
@@ -191,6 +194,7 @@ NFC
|
|||||||
Nr
|
Nr
|
||||||
NREI
|
NREI
|
||||||
number
|
number
|
||||||
|
NXP
|
||||||
OK
|
OK
|
||||||
On
|
On
|
||||||
Open
|
Open
|
||||||
@@ -215,6 +219,7 @@ Plugins
|
|||||||
POS
|
POS
|
||||||
Postfix
|
Postfix
|
||||||
PostgreSQL
|
PostgreSQL
|
||||||
|
PPRO
|
||||||
prefix
|
prefix
|
||||||
pretix
|
pretix
|
||||||
pretixdesk
|
pretixdesk
|
||||||
@@ -313,6 +318,7 @@ txt
|
|||||||
überzahlte
|
überzahlte
|
||||||
uhrzeit
|
uhrzeit
|
||||||
UID
|
UID
|
||||||
|
Ultralight
|
||||||
ungespeicherte
|
ungespeicherte
|
||||||
unkategorisiert
|
unkategorisiert
|
||||||
Unkategorisierte
|
Unkategorisierte
|
||||||
|
|||||||
+1771
-1554
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2023-06-27 12:51+0000\n"
|
"POT-Creation-Date: 2023-07-27 11:50+0000\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@@ -37,6 +37,7 @@ msgid "Venmo"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
||||||
|
#: pretix/static/pretixpresale/js/walletdetection.js:38
|
||||||
msgid "Apple Pay"
|
msgid "Apple Pay"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -134,8 +135,8 @@ msgid "Continue"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:157
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:204
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:188
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:235
|
||||||
msgid "Confirming your payment …"
|
msgid "Confirming your payment …"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -157,15 +158,15 @@ msgstr ""
|
|||||||
msgid "Total revenue"
|
msgid "Total revenue"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:12
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:13
|
||||||
msgid "Contacting Stripe …"
|
msgid "Contacting Stripe …"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:65
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:70
|
||||||
msgid "Total"
|
msgid "Total"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:164
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:211
|
||||||
msgid "Contacting your bank …"
|
msgid "Contacting your bank …"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -537,46 +538,46 @@ msgstr ""
|
|||||||
msgid "Unknown error."
|
msgid "Unknown error."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:309
|
#: pretix/static/pretixcontrol/js/ui/main.js:311
|
||||||
msgid "Your color has great contrast and is very easy to read!"
|
msgid "Your color has great contrast and is very easy to read!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:313
|
#: pretix/static/pretixcontrol/js/ui/main.js:315
|
||||||
msgid "Your color has decent contrast and is probably good-enough to read!"
|
msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:317
|
#: pretix/static/pretixcontrol/js/ui/main.js:319
|
||||||
msgid ""
|
msgid ""
|
||||||
"Your color has bad contrast for text on white background, please choose a "
|
"Your color has bad contrast for text on white background, please choose a "
|
||||||
"darker shade."
|
"darker shade."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:466
|
#: pretix/static/pretixcontrol/js/ui/main.js:468
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
#: pretix/static/pretixcontrol/js/ui/main.js:488
|
||||||
msgid "Search query"
|
msgid "Search query"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:484
|
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:485
|
#: pretix/static/pretixcontrol/js/ui/main.js:487
|
||||||
msgid "None"
|
msgid "None"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:489
|
#: pretix/static/pretixcontrol/js/ui/main.js:491
|
||||||
msgid "Selected only"
|
msgid "Selected only"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:892
|
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||||
msgid "Use a different name internally"
|
msgid "Use a different name internally"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:932
|
#: pretix/static/pretixcontrol/js/ui/main.js:934
|
||||||
msgid "Click to close"
|
msgid "Click to close"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:1007
|
#: pretix/static/pretixcontrol/js/ui/main.js:1009
|
||||||
msgid "You have unsaved changes!"
|
msgid "You have unsaved changes!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -636,23 +637,27 @@ msgstr ""
|
|||||||
msgid "Please enter the amount the organizer can keep."
|
msgid "Please enter the amount the organizer can keep."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:436
|
#: pretix/static/pretixpresale/js/ui/main.js:412
|
||||||
msgid "Please enter a quantity for one of the ticket types."
|
msgid "Please enter a quantity for one of the ticket types."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:472
|
#: pretix/static/pretixpresale/js/ui/main.js:448
|
||||||
msgid "required"
|
msgid "required"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:575
|
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:594
|
#: pretix/static/pretixpresale/js/ui/main.js:570
|
||||||
msgid "Time zone:"
|
msgid "Time zone:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:585
|
#: pretix/static/pretixpresale/js/ui/main.js:561
|
||||||
msgid "Your local time:"
|
msgid "Your local time:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixpresale/js/walletdetection.js:39
|
||||||
|
msgid "Google Pay"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Quantity"
|
msgid "Quantity"
|
||||||
|
|||||||
+1852
-1543
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2023-06-27 12:51+0000\n"
|
"POT-Creation-Date: 2023-07-21 11:46+0000\n"
|
||||||
"PO-Revision-Date: 2019-10-03 19:00+0000\n"
|
"PO-Revision-Date: 2019-10-03 19:00+0000\n"
|
||||||
"Last-Translator: Chris Spy <chrispiropoulou@hotmail.com>\n"
|
"Last-Translator: Chris Spy <chrispiropoulou@hotmail.com>\n"
|
||||||
"Language-Team: Greek <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
"Language-Team: Greek <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||||
@@ -38,6 +38,7 @@ msgid "Venmo"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
||||||
|
#: pretix/static/pretixpresale/js/walletdetection.js:38
|
||||||
msgid "Apple Pay"
|
msgid "Apple Pay"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -140,8 +141,8 @@ msgid "Continue"
|
|||||||
msgstr "Συνέχεια"
|
msgstr "Συνέχεια"
|
||||||
|
|
||||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:157
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:204
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:188
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:235
|
||||||
msgid "Confirming your payment …"
|
msgid "Confirming your payment …"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -163,15 +164,15 @@ msgstr "Πληρωμένες παραγγελίες"
|
|||||||
msgid "Total revenue"
|
msgid "Total revenue"
|
||||||
msgstr "Συνολικά κέρδη"
|
msgstr "Συνολικά κέρδη"
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:12
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:13
|
||||||
msgid "Contacting Stripe …"
|
msgid "Contacting Stripe …"
|
||||||
msgstr "Επικοινωνία με το Stripe …"
|
msgstr "Επικοινωνία με το Stripe …"
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:65
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:70
|
||||||
msgid "Total"
|
msgid "Total"
|
||||||
msgstr "Σύνολο"
|
msgstr "Σύνολο"
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:164
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:211
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
#| msgid "Contacting Stripe …"
|
#| msgid "Contacting Stripe …"
|
||||||
msgid "Contacting your bank …"
|
msgid "Contacting your bank …"
|
||||||
@@ -592,18 +593,18 @@ msgstr "Δημιουργία μηνυμάτων …"
|
|||||||
msgid "Unknown error."
|
msgid "Unknown error."
|
||||||
msgstr "Άγνωστο σφάλμα."
|
msgstr "Άγνωστο σφάλμα."
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:309
|
#: pretix/static/pretixcontrol/js/ui/main.js:311
|
||||||
msgid "Your color has great contrast and is very easy to read!"
|
msgid "Your color has great contrast and is very easy to read!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Το χρώμα σας έχει μεγάλη αντίθεση και είναι πολύ εύκολο να το διαβάσετε!"
|
"Το χρώμα σας έχει μεγάλη αντίθεση και είναι πολύ εύκολο να το διαβάσετε!"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:313
|
#: pretix/static/pretixcontrol/js/ui/main.js:315
|
||||||
msgid "Your color has decent contrast and is probably good-enough to read!"
|
msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Το χρώμα σας έχει αξιοπρεπή αντίθεση και είναι ίσως αρκετά καλό για να "
|
"Το χρώμα σας έχει αξιοπρεπή αντίθεση και είναι ίσως αρκετά καλό για να "
|
||||||
"διαβάσετε!"
|
"διαβάσετε!"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:317
|
#: pretix/static/pretixcontrol/js/ui/main.js:319
|
||||||
msgid ""
|
msgid ""
|
||||||
"Your color has bad contrast for text on white background, please choose a "
|
"Your color has bad contrast for text on white background, please choose a "
|
||||||
"darker shade."
|
"darker shade."
|
||||||
@@ -611,32 +612,32 @@ msgstr ""
|
|||||||
"Το χρώμα σας έχει κακή αντίθεση για κείμενο σε λευκό φόντο, επιλέξτε μια πιο "
|
"Το χρώμα σας έχει κακή αντίθεση για κείμενο σε λευκό φόντο, επιλέξτε μια πιο "
|
||||||
"σκούρα σκιά."
|
"σκούρα σκιά."
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:466
|
#: pretix/static/pretixcontrol/js/ui/main.js:468
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
#: pretix/static/pretixcontrol/js/ui/main.js:488
|
||||||
msgid "Search query"
|
msgid "Search query"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:484
|
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr "Όλα"
|
msgstr "Όλα"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:485
|
#: pretix/static/pretixcontrol/js/ui/main.js:487
|
||||||
msgid "None"
|
msgid "None"
|
||||||
msgstr "Κανένας"
|
msgstr "Κανένας"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:489
|
#: pretix/static/pretixcontrol/js/ui/main.js:491
|
||||||
msgid "Selected only"
|
msgid "Selected only"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:892
|
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||||
msgid "Use a different name internally"
|
msgid "Use a different name internally"
|
||||||
msgstr "Χρησιμοποιήστε διαφορετικό όνομα εσωτερικά"
|
msgstr "Χρησιμοποιήστε διαφορετικό όνομα εσωτερικά"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:932
|
#: pretix/static/pretixcontrol/js/ui/main.js:934
|
||||||
msgid "Click to close"
|
msgid "Click to close"
|
||||||
msgstr "Κάντε κλικ για να κλείσετε"
|
msgstr "Κάντε κλικ για να κλείσετε"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:1007
|
#: pretix/static/pretixcontrol/js/ui/main.js:1009
|
||||||
msgid "You have unsaved changes!"
|
msgid "You have unsaved changes!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -708,25 +709,29 @@ msgstr "απο %(currency)s %(price)s"
|
|||||||
msgid "Please enter the amount the organizer can keep."
|
msgid "Please enter the amount the organizer can keep."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:436
|
#: pretix/static/pretixpresale/js/ui/main.js:412
|
||||||
msgid "Please enter a quantity for one of the ticket types."
|
msgid "Please enter a quantity for one of the ticket types."
|
||||||
msgstr "Εισαγάγετε μια ποσότητα για έναν από τους τύπους εισιτηρίων."
|
msgstr "Εισαγάγετε μια ποσότητα για έναν από τους τύπους εισιτηρίων."
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:472
|
#: pretix/static/pretixpresale/js/ui/main.js:448
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
#| msgid "Cart expired"
|
#| msgid "Cart expired"
|
||||||
msgid "required"
|
msgid "required"
|
||||||
msgstr "Το καλάθι έληξε"
|
msgstr "Το καλάθι έληξε"
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:575
|
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:594
|
#: pretix/static/pretixpresale/js/ui/main.js:570
|
||||||
msgid "Time zone:"
|
msgid "Time zone:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:585
|
#: pretix/static/pretixpresale/js/ui/main.js:561
|
||||||
msgid "Your local time:"
|
msgid "Your local time:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixpresale/js/walletdetection.js:39
|
||||||
|
msgid "Google Pay"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Quantity"
|
msgid "Quantity"
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
+2141
-2037
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2023-06-27 12:51+0000\n"
|
"POT-Creation-Date: 2023-07-21 11:46+0000\n"
|
||||||
"PO-Revision-Date: 2021-11-25 21:00+0000\n"
|
"PO-Revision-Date: 2021-11-25 21:00+0000\n"
|
||||||
"Last-Translator: Ismael Menéndez Fernández <ismael.menendez@balidea.com>\n"
|
"Last-Translator: Ismael Menéndez Fernández <ismael.menendez@balidea.com>\n"
|
||||||
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/pretix-"
|
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||||
@@ -38,6 +38,7 @@ msgid "Venmo"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
||||||
|
#: pretix/static/pretixpresale/js/walletdetection.js:38
|
||||||
msgid "Apple Pay"
|
msgid "Apple Pay"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -137,8 +138,8 @@ msgid "Continue"
|
|||||||
msgstr "Continuar"
|
msgstr "Continuar"
|
||||||
|
|
||||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:157
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:204
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:188
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:235
|
||||||
msgid "Confirming your payment …"
|
msgid "Confirming your payment …"
|
||||||
msgstr "Confirmando el pago…"
|
msgstr "Confirmando el pago…"
|
||||||
|
|
||||||
@@ -160,15 +161,15 @@ msgstr "Órdenes pagadas"
|
|||||||
msgid "Total revenue"
|
msgid "Total revenue"
|
||||||
msgstr "Ingresos totales"
|
msgstr "Ingresos totales"
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:12
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:13
|
||||||
msgid "Contacting Stripe …"
|
msgid "Contacting Stripe …"
|
||||||
msgstr "Contactando con Stripe…"
|
msgstr "Contactando con Stripe…"
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:65
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:70
|
||||||
msgid "Total"
|
msgid "Total"
|
||||||
msgstr "Total"
|
msgstr "Total"
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:164
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:211
|
||||||
msgid "Contacting your bank …"
|
msgid "Contacting your bank …"
|
||||||
msgstr "Contactando con el banco…"
|
msgstr "Contactando con el banco…"
|
||||||
|
|
||||||
@@ -559,17 +560,17 @@ msgstr "Generando mensajes…"
|
|||||||
msgid "Unknown error."
|
msgid "Unknown error."
|
||||||
msgstr "Error desconocido."
|
msgstr "Error desconocido."
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:309
|
#: pretix/static/pretixcontrol/js/ui/main.js:311
|
||||||
msgid "Your color has great contrast and is very easy to read!"
|
msgid "Your color has great contrast and is very easy to read!"
|
||||||
msgstr "¡Tu color tiene gran contraste y es muy fácil de leer!"
|
msgstr "¡Tu color tiene gran contraste y es muy fácil de leer!"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:313
|
#: pretix/static/pretixcontrol/js/ui/main.js:315
|
||||||
msgid "Your color has decent contrast and is probably good-enough to read!"
|
msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"¡Tu color tiene un contraste decente y es probablemente lo suficientemente "
|
"¡Tu color tiene un contraste decente y es probablemente lo suficientemente "
|
||||||
"legible!"
|
"legible!"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:317
|
#: pretix/static/pretixcontrol/js/ui/main.js:319
|
||||||
msgid ""
|
msgid ""
|
||||||
"Your color has bad contrast for text on white background, please choose a "
|
"Your color has bad contrast for text on white background, please choose a "
|
||||||
"darker shade."
|
"darker shade."
|
||||||
@@ -577,32 +578,32 @@ msgstr ""
|
|||||||
"Tu color tiene mal contraste para un texto con fondo blanco, por favor, "
|
"Tu color tiene mal contraste para un texto con fondo blanco, por favor, "
|
||||||
"escoge un tono más oscuro."
|
"escoge un tono más oscuro."
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:466
|
#: pretix/static/pretixcontrol/js/ui/main.js:468
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
#: pretix/static/pretixcontrol/js/ui/main.js:488
|
||||||
msgid "Search query"
|
msgid "Search query"
|
||||||
msgstr "Consultar búsqueda"
|
msgstr "Consultar búsqueda"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:484
|
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr "Todos"
|
msgstr "Todos"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:485
|
#: pretix/static/pretixcontrol/js/ui/main.js:487
|
||||||
msgid "None"
|
msgid "None"
|
||||||
msgstr "Ninguno"
|
msgstr "Ninguno"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:489
|
#: pretix/static/pretixcontrol/js/ui/main.js:491
|
||||||
msgid "Selected only"
|
msgid "Selected only"
|
||||||
msgstr "Solamente seleccionados"
|
msgstr "Solamente seleccionados"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:892
|
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||||
msgid "Use a different name internally"
|
msgid "Use a different name internally"
|
||||||
msgstr "Usar un nombre diferente internamente"
|
msgstr "Usar un nombre diferente internamente"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:932
|
#: pretix/static/pretixcontrol/js/ui/main.js:934
|
||||||
msgid "Click to close"
|
msgid "Click to close"
|
||||||
msgstr "Click para cerrar"
|
msgstr "Click para cerrar"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:1007
|
#: pretix/static/pretixcontrol/js/ui/main.js:1009
|
||||||
msgid "You have unsaved changes!"
|
msgid "You have unsaved changes!"
|
||||||
msgstr "¡Tienes cambios sin guardar!"
|
msgstr "¡Tienes cambios sin guardar!"
|
||||||
|
|
||||||
@@ -666,23 +667,27 @@ msgstr "Obtienes %(currency)s %(price)s de vuelta"
|
|||||||
msgid "Please enter the amount the organizer can keep."
|
msgid "Please enter the amount the organizer can keep."
|
||||||
msgstr "Por favor, ingrese el monto que el organizador puede quedarse."
|
msgstr "Por favor, ingrese el monto que el organizador puede quedarse."
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:436
|
#: pretix/static/pretixpresale/js/ui/main.js:412
|
||||||
msgid "Please enter a quantity for one of the ticket types."
|
msgid "Please enter a quantity for one of the ticket types."
|
||||||
msgstr "Por favor, introduzca un valor para cada tipo de entrada."
|
msgstr "Por favor, introduzca un valor para cada tipo de entrada."
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:472
|
#: pretix/static/pretixpresale/js/ui/main.js:448
|
||||||
msgid "required"
|
msgid "required"
|
||||||
msgstr "campo requerido"
|
msgstr "campo requerido"
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:575
|
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:594
|
#: pretix/static/pretixpresale/js/ui/main.js:570
|
||||||
msgid "Time zone:"
|
msgid "Time zone:"
|
||||||
msgstr "Zona horaria:"
|
msgstr "Zona horaria:"
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:585
|
#: pretix/static/pretixpresale/js/ui/main.js:561
|
||||||
msgid "Your local time:"
|
msgid "Your local time:"
|
||||||
msgstr "Su hora local:"
|
msgstr "Su hora local:"
|
||||||
|
|
||||||
|
#: pretix/static/pretixpresale/js/walletdetection.js:39
|
||||||
|
msgid "Google Pay"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Quantity"
|
msgid "Quantity"
|
||||||
|
|||||||
+1813
-1547
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2023-06-27 12:51+0000\n"
|
"POT-Creation-Date: 2023-07-21 11:46+0000\n"
|
||||||
"PO-Revision-Date: 2021-11-10 05:00+0000\n"
|
"PO-Revision-Date: 2021-11-10 05:00+0000\n"
|
||||||
"Last-Translator: Jaakko Rinta-Filppula <jaakko@r-f.fi>\n"
|
"Last-Translator: Jaakko Rinta-Filppula <jaakko@r-f.fi>\n"
|
||||||
"Language-Team: Finnish <https://translate.pretix.eu/projects/pretix/pretix-"
|
"Language-Team: Finnish <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||||
@@ -38,6 +38,7 @@ msgid "Venmo"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
||||||
|
#: pretix/static/pretixpresale/js/walletdetection.js:38
|
||||||
msgid "Apple Pay"
|
msgid "Apple Pay"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -140,8 +141,8 @@ msgid "Continue"
|
|||||||
msgstr "Jatka"
|
msgstr "Jatka"
|
||||||
|
|
||||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:157
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:204
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:188
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:235
|
||||||
msgid "Confirming your payment …"
|
msgid "Confirming your payment …"
|
||||||
msgstr "Maksuasi vahvistetaan …"
|
msgstr "Maksuasi vahvistetaan …"
|
||||||
|
|
||||||
@@ -163,15 +164,15 @@ msgstr "Maksetut tilaukset"
|
|||||||
msgid "Total revenue"
|
msgid "Total revenue"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:12
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:13
|
||||||
msgid "Contacting Stripe …"
|
msgid "Contacting Stripe …"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:65
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:70
|
||||||
msgid "Total"
|
msgid "Total"
|
||||||
msgstr "Summa"
|
msgstr "Summa"
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:164
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:211
|
||||||
msgid "Contacting your bank …"
|
msgid "Contacting your bank …"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -562,46 +563,46 @@ msgstr ""
|
|||||||
msgid "Unknown error."
|
msgid "Unknown error."
|
||||||
msgstr "Tuntematon virhe."
|
msgstr "Tuntematon virhe."
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:309
|
#: pretix/static/pretixcontrol/js/ui/main.js:311
|
||||||
msgid "Your color has great contrast and is very easy to read!"
|
msgid "Your color has great contrast and is very easy to read!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:313
|
#: pretix/static/pretixcontrol/js/ui/main.js:315
|
||||||
msgid "Your color has decent contrast and is probably good-enough to read!"
|
msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:317
|
#: pretix/static/pretixcontrol/js/ui/main.js:319
|
||||||
msgid ""
|
msgid ""
|
||||||
"Your color has bad contrast for text on white background, please choose a "
|
"Your color has bad contrast for text on white background, please choose a "
|
||||||
"darker shade."
|
"darker shade."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:466
|
#: pretix/static/pretixcontrol/js/ui/main.js:468
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
#: pretix/static/pretixcontrol/js/ui/main.js:488
|
||||||
msgid "Search query"
|
msgid "Search query"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:484
|
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr "Kaikki"
|
msgstr "Kaikki"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:485
|
#: pretix/static/pretixcontrol/js/ui/main.js:487
|
||||||
msgid "None"
|
msgid "None"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:489
|
#: pretix/static/pretixcontrol/js/ui/main.js:491
|
||||||
msgid "Selected only"
|
msgid "Selected only"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:892
|
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||||
msgid "Use a different name internally"
|
msgid "Use a different name internally"
|
||||||
msgstr "Käytä toista nimeä sisäisesti"
|
msgstr "Käytä toista nimeä sisäisesti"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:932
|
#: pretix/static/pretixcontrol/js/ui/main.js:934
|
||||||
msgid "Click to close"
|
msgid "Click to close"
|
||||||
msgstr "Sulje klikkaamalla"
|
msgstr "Sulje klikkaamalla"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:1007
|
#: pretix/static/pretixcontrol/js/ui/main.js:1009
|
||||||
msgid "You have unsaved changes!"
|
msgid "You have unsaved changes!"
|
||||||
msgstr "Sinulla on tallentamattomia muutoksia!"
|
msgstr "Sinulla on tallentamattomia muutoksia!"
|
||||||
|
|
||||||
@@ -665,25 +666,29 @@ msgstr ""
|
|||||||
msgid "Please enter the amount the organizer can keep."
|
msgid "Please enter the amount the organizer can keep."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:436
|
#: pretix/static/pretixpresale/js/ui/main.js:412
|
||||||
msgid "Please enter a quantity for one of the ticket types."
|
msgid "Please enter a quantity for one of the ticket types."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:472
|
#: pretix/static/pretixpresale/js/ui/main.js:448
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
#| msgid "Cart expired"
|
#| msgid "Cart expired"
|
||||||
msgid "required"
|
msgid "required"
|
||||||
msgstr "Ostoskori on vanhentunut"
|
msgstr "Ostoskori on vanhentunut"
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:575
|
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:594
|
#: pretix/static/pretixpresale/js/ui/main.js:570
|
||||||
msgid "Time zone:"
|
msgid "Time zone:"
|
||||||
msgstr "Aikavyöhyke:"
|
msgstr "Aikavyöhyke:"
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:585
|
#: pretix/static/pretixpresale/js/ui/main.js:561
|
||||||
msgid "Your local time:"
|
msgid "Your local time:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixpresale/js/walletdetection.js:39
|
||||||
|
msgid "Google Pay"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Quantity"
|
msgid "Quantity"
|
||||||
|
|||||||
+1876
-1603
File diff suppressed because it is too large
Load Diff
@@ -6,8 +6,8 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: French\n"
|
"Project-Id-Version: French\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2023-06-27 12:51+0000\n"
|
"POT-Creation-Date: 2023-07-21 11:46+0000\n"
|
||||||
"PO-Revision-Date: 2023-06-17 09:09+0000\n"
|
"PO-Revision-Date: 2023-08-02 02:00+0000\n"
|
||||||
"Last-Translator: Ronan LE MEILLAT <ronan.le_meillat@highcanfly.club>\n"
|
"Last-Translator: Ronan LE MEILLAT <ronan.le_meillat@highcanfly.club>\n"
|
||||||
"Language-Team: French <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
"Language-Team: French <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||||
"fr/>\n"
|
"fr/>\n"
|
||||||
@@ -16,7 +16,7 @@ msgstr ""
|
|||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=n > 1;\n"
|
"Plural-Forms: nplurals=2; plural=n > 1;\n"
|
||||||
"X-Generator: Weblate 4.17\n"
|
"X-Generator: Weblate 4.18.2\n"
|
||||||
|
|
||||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
|
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
|
||||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
|
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
|
||||||
@@ -37,6 +37,7 @@ msgid "Venmo"
|
|||||||
msgstr "Venmo"
|
msgstr "Venmo"
|
||||||
|
|
||||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
||||||
|
#: pretix/static/pretixpresale/js/walletdetection.js:38
|
||||||
msgid "Apple Pay"
|
msgid "Apple Pay"
|
||||||
msgstr "Apple Pay"
|
msgstr "Apple Pay"
|
||||||
|
|
||||||
@@ -62,7 +63,7 @@ msgstr "iDEAL"
|
|||||||
|
|
||||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:42
|
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:42
|
||||||
msgid "SEPA Direct Debit"
|
msgid "SEPA Direct Debit"
|
||||||
msgstr "Débit direct SEPA"
|
msgstr "Prélèvement SEPA"
|
||||||
|
|
||||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:43
|
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:43
|
||||||
msgid "Bancontact"
|
msgid "Bancontact"
|
||||||
@@ -134,8 +135,8 @@ msgid "Continue"
|
|||||||
msgstr "Continuer"
|
msgstr "Continuer"
|
||||||
|
|
||||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:157
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:204
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:188
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:235
|
||||||
msgid "Confirming your payment …"
|
msgid "Confirming your payment …"
|
||||||
msgstr "Confirmation de votre paiement…"
|
msgstr "Confirmation de votre paiement…"
|
||||||
|
|
||||||
@@ -157,15 +158,15 @@ msgstr "Commandes payées"
|
|||||||
msgid "Total revenue"
|
msgid "Total revenue"
|
||||||
msgstr "Total des revenus"
|
msgstr "Total des revenus"
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:12
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:13
|
||||||
msgid "Contacting Stripe …"
|
msgid "Contacting Stripe …"
|
||||||
msgstr "Communication avec Stripe …"
|
msgstr "Communication avec Stripe …"
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:65
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:70
|
||||||
msgid "Total"
|
msgid "Total"
|
||||||
msgstr "Total"
|
msgstr "Total"
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:164
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:211
|
||||||
msgid "Contacting your bank …"
|
msgid "Contacting your bank …"
|
||||||
msgstr "Communication avec votre banque …"
|
msgstr "Communication avec votre banque …"
|
||||||
|
|
||||||
@@ -281,7 +282,7 @@ msgstr "Entrée non autorisée"
|
|||||||
|
|
||||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:60
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:60
|
||||||
msgid "Ticket code revoked/changed"
|
msgid "Ticket code revoked/changed"
|
||||||
msgstr "Code du billet révoqué/changé"
|
msgstr "Code du billet révoqué/modifié"
|
||||||
|
|
||||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:61
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:61
|
||||||
msgid "Ticket blocked"
|
msgid "Ticket blocked"
|
||||||
@@ -555,16 +556,16 @@ msgstr "Création de messages …"
|
|||||||
msgid "Unknown error."
|
msgid "Unknown error."
|
||||||
msgstr "Erreur inconnue."
|
msgstr "Erreur inconnue."
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:309
|
#: pretix/static/pretixcontrol/js/ui/main.js:311
|
||||||
msgid "Your color has great contrast and is very easy to read!"
|
msgid "Your color has great contrast and is very easy to read!"
|
||||||
msgstr "Votre choix couleur a un bon contraste et il est très facile à lire !"
|
msgstr "Votre choix couleur a un bon contraste et il est très facile à lire !"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:313
|
#: pretix/static/pretixcontrol/js/ui/main.js:315
|
||||||
msgid "Your color has decent contrast and is probably good-enough to read!"
|
msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Votre choix de couleur est assez bon pour la lecture et a un bon contraste !"
|
"Votre choix de couleur est assez bon pour la lecture et a un bon contraste !"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:317
|
#: pretix/static/pretixcontrol/js/ui/main.js:319
|
||||||
msgid ""
|
msgid ""
|
||||||
"Your color has bad contrast for text on white background, please choose a "
|
"Your color has bad contrast for text on white background, please choose a "
|
||||||
"darker shade."
|
"darker shade."
|
||||||
@@ -572,32 +573,32 @@ msgstr ""
|
|||||||
"Votre choix de couleur n'a pas un bon contraste avec du texte sur un fond "
|
"Votre choix de couleur n'a pas un bon contraste avec du texte sur un fond "
|
||||||
"blanc, SVP choisissez un ton plus sombre."
|
"blanc, SVP choisissez un ton plus sombre."
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:466
|
#: pretix/static/pretixcontrol/js/ui/main.js:468
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
#: pretix/static/pretixcontrol/js/ui/main.js:488
|
||||||
msgid "Search query"
|
msgid "Search query"
|
||||||
msgstr "Requête de recherche"
|
msgstr "Requête de recherche"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:484
|
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr "Tous"
|
msgstr "Tous"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:485
|
#: pretix/static/pretixcontrol/js/ui/main.js:487
|
||||||
msgid "None"
|
msgid "None"
|
||||||
msgstr "Aucun"
|
msgstr "Aucun"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:489
|
#: pretix/static/pretixcontrol/js/ui/main.js:491
|
||||||
msgid "Selected only"
|
msgid "Selected only"
|
||||||
msgstr "Seuls les sélectionnés"
|
msgstr "Seuls les sélectionnés"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:892
|
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||||
msgid "Use a different name internally"
|
msgid "Use a different name internally"
|
||||||
msgstr "Utiliser un nom différent en interne"
|
msgstr "Utiliser un nom différent en interne"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:932
|
#: pretix/static/pretixcontrol/js/ui/main.js:934
|
||||||
msgid "Click to close"
|
msgid "Click to close"
|
||||||
msgstr "Cliquez pour fermer"
|
msgstr "Cliquez pour fermer"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:1007
|
#: pretix/static/pretixcontrol/js/ui/main.js:1009
|
||||||
msgid "You have unsaved changes!"
|
msgid "You have unsaved changes!"
|
||||||
msgstr "Vous avez des modifications non sauvegardées !"
|
msgstr "Vous avez des modifications non sauvegardées !"
|
||||||
|
|
||||||
@@ -660,23 +661,27 @@ msgid "Please enter the amount the organizer can keep."
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Veuillez indiquer le montant que l'organisateur est autorisé à retenir."
|
"Veuillez indiquer le montant que l'organisateur est autorisé à retenir."
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:436
|
#: pretix/static/pretixpresale/js/ui/main.js:412
|
||||||
msgid "Please enter a quantity for one of the ticket types."
|
msgid "Please enter a quantity for one of the ticket types."
|
||||||
msgstr "SVP entrez une quantité pour un de vos types de billets."
|
msgstr "SVP entrez une quantité pour un de vos types de billets."
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:472
|
#: pretix/static/pretixpresale/js/ui/main.js:448
|
||||||
msgid "required"
|
msgid "required"
|
||||||
msgstr "obligatoire"
|
msgstr "obligatoire"
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:575
|
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:594
|
#: pretix/static/pretixpresale/js/ui/main.js:570
|
||||||
msgid "Time zone:"
|
msgid "Time zone:"
|
||||||
msgstr "Fuseau horaire :"
|
msgstr "Fuseau horaire :"
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:585
|
#: pretix/static/pretixpresale/js/ui/main.js:561
|
||||||
msgid "Your local time:"
|
msgid "Your local time:"
|
||||||
msgstr "Votre heure locale :"
|
msgstr "Votre heure locale :"
|
||||||
|
|
||||||
|
#: pretix/static/pretixpresale/js/walletdetection.js:39
|
||||||
|
msgid "Google Pay"
|
||||||
|
msgstr "Google Pay"
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Quantity"
|
msgid "Quantity"
|
||||||
|
|||||||
+1793
-1530
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2023-06-27 12:51+0000\n"
|
"POT-Creation-Date: 2023-07-21 11:46+0000\n"
|
||||||
"PO-Revision-Date: 2022-02-22 22:00+0000\n"
|
"PO-Revision-Date: 2022-02-22 22:00+0000\n"
|
||||||
"Last-Translator: Ismael Menéndez Fernández <ismael.menendez@balidea.com>\n"
|
"Last-Translator: Ismael Menéndez Fernández <ismael.menendez@balidea.com>\n"
|
||||||
"Language-Team: Galician <https://translate.pretix.eu/projects/pretix/pretix-"
|
"Language-Team: Galician <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||||
@@ -38,6 +38,7 @@ msgid "Venmo"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
||||||
|
#: pretix/static/pretixpresale/js/walletdetection.js:38
|
||||||
msgid "Apple Pay"
|
msgid "Apple Pay"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -137,8 +138,8 @@ msgid "Continue"
|
|||||||
msgstr "Continuar"
|
msgstr "Continuar"
|
||||||
|
|
||||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:157
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:204
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:188
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:235
|
||||||
msgid "Confirming your payment …"
|
msgid "Confirming your payment …"
|
||||||
msgstr "Confirmando o pagamento…"
|
msgstr "Confirmando o pagamento…"
|
||||||
|
|
||||||
@@ -160,15 +161,15 @@ msgstr "Pedidos pagados"
|
|||||||
msgid "Total revenue"
|
msgid "Total revenue"
|
||||||
msgstr "Ingresos totais"
|
msgstr "Ingresos totais"
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:12
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:13
|
||||||
msgid "Contacting Stripe …"
|
msgid "Contacting Stripe …"
|
||||||
msgstr "Contactando con Stripe…"
|
msgstr "Contactando con Stripe…"
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:65
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:70
|
||||||
msgid "Total"
|
msgid "Total"
|
||||||
msgstr "Total"
|
msgstr "Total"
|
||||||
|
|
||||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:164
|
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:211
|
||||||
msgid "Contacting your bank …"
|
msgid "Contacting your bank …"
|
||||||
msgstr "Contactando co banco…"
|
msgstr "Contactando co banco…"
|
||||||
|
|
||||||
@@ -557,17 +558,17 @@ msgstr "Xerando mensaxes…"
|
|||||||
msgid "Unknown error."
|
msgid "Unknown error."
|
||||||
msgstr "Erro descoñecido."
|
msgstr "Erro descoñecido."
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:309
|
#: pretix/static/pretixcontrol/js/ui/main.js:311
|
||||||
msgid "Your color has great contrast and is very easy to read!"
|
msgid "Your color has great contrast and is very easy to read!"
|
||||||
msgstr "A túa cor ten moito contraste e é moi doada de ler!"
|
msgstr "A túa cor ten moito contraste e é moi doada de ler!"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:313
|
#: pretix/static/pretixcontrol/js/ui/main.js:315
|
||||||
msgid "Your color has decent contrast and is probably good-enough to read!"
|
msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"A túa cor ten un contraste axeitado e probablemente sexa suficientemente "
|
"A túa cor ten un contraste axeitado e probablemente sexa suficientemente "
|
||||||
"lexible!"
|
"lexible!"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:317
|
#: pretix/static/pretixcontrol/js/ui/main.js:319
|
||||||
msgid ""
|
msgid ""
|
||||||
"Your color has bad contrast for text on white background, please choose a "
|
"Your color has bad contrast for text on white background, please choose a "
|
||||||
"darker shade."
|
"darker shade."
|
||||||
@@ -575,32 +576,32 @@ msgstr ""
|
|||||||
"A túa cor ten mal contraste para un texto con fondo branco. Por favor, "
|
"A túa cor ten mal contraste para un texto con fondo branco. Por favor, "
|
||||||
"escolle un ton máis escuro."
|
"escolle un ton máis escuro."
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:466
|
#: pretix/static/pretixcontrol/js/ui/main.js:468
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
#: pretix/static/pretixcontrol/js/ui/main.js:488
|
||||||
msgid "Search query"
|
msgid "Search query"
|
||||||
msgstr "Consultar unha procura"
|
msgstr "Consultar unha procura"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:484
|
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr "Todos"
|
msgstr "Todos"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:485
|
#: pretix/static/pretixcontrol/js/ui/main.js:487
|
||||||
msgid "None"
|
msgid "None"
|
||||||
msgstr "Ningún"
|
msgstr "Ningún"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:489
|
#: pretix/static/pretixcontrol/js/ui/main.js:491
|
||||||
msgid "Selected only"
|
msgid "Selected only"
|
||||||
msgstr "Soamente seleccionados"
|
msgstr "Soamente seleccionados"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:892
|
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||||
msgid "Use a different name internally"
|
msgid "Use a different name internally"
|
||||||
msgstr "Usar un nome diferente internamente"
|
msgstr "Usar un nome diferente internamente"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:932
|
#: pretix/static/pretixcontrol/js/ui/main.js:934
|
||||||
msgid "Click to close"
|
msgid "Click to close"
|
||||||
msgstr "Click para cerrar"
|
msgstr "Click para cerrar"
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:1007
|
#: pretix/static/pretixcontrol/js/ui/main.js:1009
|
||||||
msgid "You have unsaved changes!"
|
msgid "You have unsaved changes!"
|
||||||
msgstr "Tes cambios sen gardar!"
|
msgstr "Tes cambios sen gardar!"
|
||||||
|
|
||||||
@@ -663,23 +664,27 @@ msgstr "Obtés %(currency)s %(price)s de volta"
|
|||||||
msgid "Please enter the amount the organizer can keep."
|
msgid "Please enter the amount the organizer can keep."
|
||||||
msgstr "Por favor, ingrese a cantidade que pode conservar o organizador."
|
msgstr "Por favor, ingrese a cantidade que pode conservar o organizador."
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:436
|
#: pretix/static/pretixpresale/js/ui/main.js:412
|
||||||
msgid "Please enter a quantity for one of the ticket types."
|
msgid "Please enter a quantity for one of the ticket types."
|
||||||
msgstr "Por favor, introduza un valor para cada tipo de entrada."
|
msgstr "Por favor, introduza un valor para cada tipo de entrada."
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:472
|
#: pretix/static/pretixpresale/js/ui/main.js:448
|
||||||
msgid "required"
|
msgid "required"
|
||||||
msgstr "campo requirido"
|
msgstr "campo requirido"
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:575
|
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:594
|
#: pretix/static/pretixpresale/js/ui/main.js:570
|
||||||
msgid "Time zone:"
|
msgid "Time zone:"
|
||||||
msgstr "Zona horaria:"
|
msgstr "Zona horaria:"
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/ui/main.js:585
|
#: pretix/static/pretixpresale/js/ui/main.js:561
|
||||||
msgid "Your local time:"
|
msgid "Your local time:"
|
||||||
msgstr "A súa hora local:"
|
msgstr "A súa hora local:"
|
||||||
|
|
||||||
|
#: pretix/static/pretixpresale/js/walletdetection.js:39
|
||||||
|
msgid "Google Pay"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Quantity"
|
msgid "Quantity"
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user