forked from CGM_Public/pretix_original
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7fd683b5c8 |
+2
-1
@@ -33,7 +33,8 @@ RUN apt-get update && \
|
||||
mkdir /static && \
|
||||
mkdir /etc/supervisord && \
|
||||
curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash - && \
|
||||
apt-get install -y nodejs
|
||||
apt-get install -y nodejs && \
|
||||
curl -qL https://www.npmjs.com/install.sh | sh
|
||||
|
||||
|
||||
ENV LC_ALL=C.UTF-8 \
|
||||
|
||||
@@ -152,10 +152,6 @@ Example::
|
||||
password=abcd
|
||||
host=localhost
|
||||
port=3306
|
||||
sslmode=require
|
||||
sslrootcert=/etc/pretix/postgresql-ca.crt
|
||||
sslcert=/etc/pretix/postgresql-client-crt.crt
|
||||
sslkey=/etc/pretix/postgresql-client-key.key
|
||||
|
||||
``backend``
|
||||
One of ``sqlite3`` and ``postgresql``.
|
||||
@@ -167,11 +163,6 @@ Example::
|
||||
``user``, ``password``, ``host``, ``port``
|
||||
Connection details for the database connection. Empty by default.
|
||||
|
||||
``sslmode``, ``sslrootcert``
|
||||
Connection TLS details for the PostgreSQL database connection. Possible values of ``sslmode`` are ``disable``, ``allow``, ``prefer``, ``require``, ``verify-ca``, and ``verify-full``. ``sslrootcert`` should be the accessible path of the ca certificate. Both values are empty by default.
|
||||
|
||||
``sslcert``, ``sslkey``
|
||||
Connection mTLS details for the PostgreSQL database connection. It's also necessary to specify ``sslmode`` and ``sslrootcert`` parameters, please check the correct values from the TLS part. ``sslcert`` should be the accessible path of the client certificate. ``sslkey`` should be the accessible path of the client key. All values are empty by default.
|
||||
.. _`config-replica`:
|
||||
|
||||
Database replica settings
|
||||
@@ -333,10 +324,6 @@ to speed up various operations::
|
||||
["sentinel_host_3", 26379]
|
||||
]
|
||||
password=password
|
||||
ssl_cert_reqs=required
|
||||
ssl_ca_certs=/etc/pretix/redis-ca.pem
|
||||
ssl_keyfile=/etc/pretix/redis-client-crt.pem
|
||||
ssl_certfile=/etc/pretix/redis-client-key.key
|
||||
|
||||
``location``
|
||||
The location of redis, as a URL of the form ``redis://[:password]@localhost:6379/0``
|
||||
@@ -360,22 +347,6 @@ to speed up various operations::
|
||||
If your redis setup doesn't require a password or you already specified it in the location you can omit this option.
|
||||
If this is set it will be passed to redis as the connection option PASSWORD.
|
||||
|
||||
``ssl_cert_reqs``
|
||||
If this is set it will be passed to redis as the connection option ``SSL_CERT_REQS``.
|
||||
Possible values are ``none``, ``optional``, and ``required``.
|
||||
|
||||
``ssl_ca_certs``
|
||||
If your redis setup doesn't require TLS you can omit this option.
|
||||
If this is set it will be passed to redis as the connection option ``SSL_CA_CERTS``. Possible value is the ca path.
|
||||
|
||||
``ssl_keyfile``
|
||||
If your redis setup doesn't require mTLS you can omit this option.
|
||||
If this is set it will be passed to redis as the connection option ``SSL_KEYFILE``. Possible value is the keyfile path.
|
||||
|
||||
``ssl_certfile``
|
||||
If your redis setup doesn't require mTLS you can omit this option.
|
||||
If this is set it will be passed to redis as the connection option ``SSL_CERTFILE``. Possible value is the certfile path.
|
||||
|
||||
If redis is not configured, pretix will store sessions and locks in the database. If memcached
|
||||
is configured, memcached will be used for caching instead of redis.
|
||||
|
||||
@@ -425,8 +396,6 @@ The two ``transport_options`` entries can be omitted in most cases.
|
||||
If they are present they need to be a valid JSON dictionary.
|
||||
For possible entries in that dictionary see the `Celery documentation`_.
|
||||
|
||||
It is possible the use Redis with TLS/mTLS for the broker or the backend. To do so, it is necessary to specify the TLS identifier ``rediss``, the ssl mode ``ssl_cert_reqs`` and optionally specify the CA (TLS) ``ssl_ca_certs``, cert ``ssl_certfile`` and key ``ssl_keyfile`` (mTLS) path as encoded string. the following uri describes the format and possible parameters ``rediss://0.0.0.0:6379/1?ssl_cert_reqs=required&ssl_ca_certs=%2Fetc%2Fpretix%2Fredis-ca.pem&ssl_certfile=%2Fetc%2Fpretix%2Fredis-client-crt.pem&ssl_keyfile=%2Fetc%2Fpretix%2Fredis-client-key.key``
|
||||
|
||||
To use redis with sentinels set the broker or backend to ``sentinel://sentinel_host_1:26379;sentinel_host_2:26379/0``
|
||||
and the respective transport_options to ``{"master_name":"mymaster"}``.
|
||||
If your redis instances behind the sentinel have a password use ``sentinel://:my_password@sentinel_host_1:26379;sentinel_host_2:26379/0``.
|
||||
|
||||
@@ -68,7 +68,7 @@ generated key and installs the plugin from the URL we told you::
|
||||
mkdir -p /etc/ssh && \
|
||||
ssh-keyscan -t rsa -p 10022 code.rami.io >> /root/.ssh/known_hosts && \
|
||||
echo StrictHostKeyChecking=no >> /root/.ssh/config && \
|
||||
DJANGO_SETTINGS_MODULE= pip3 install -U "git+ssh://git@code.rami.io:10022/pretix/pretix-slack.git@stable#egg=pretix-slack" && \
|
||||
DJANGO_SETTINGS_MODULE=pretix.settings pip3 install -U "git+ssh://git@code.rami.io:10022/pretix/pretix-slack.git@stable#egg=pretix-slack" && \
|
||||
cd /pretix/src && \
|
||||
sudo -u pretixuser make production
|
||||
USER pretixuser
|
||||
|
||||
+2
-30
@@ -32,16 +32,10 @@ as well as the type of underlying hardware. Example:
|
||||
"token": "kpp4jn8g2ynzonp6",
|
||||
"hardware_brand": "Samsung",
|
||||
"hardware_model": "Galaxy S",
|
||||
"os_name": "Android",
|
||||
"os_version": "2.3.6",
|
||||
"software_brand": "pretixdroid",
|
||||
"software_version": "4.0.0",
|
||||
"rsa_pubkey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqh…nswIDAQAB\n-----END PUBLIC KEY-----\n"
|
||||
"software_version": "4.0.0"
|
||||
}
|
||||
|
||||
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
|
||||
information on your device as well as your API token:
|
||||
|
||||
@@ -104,8 +98,6 @@ following endpoint:
|
||||
{
|
||||
"hardware_brand": "Samsung",
|
||||
"hardware_model": "Galaxy S",
|
||||
"os_name": "Android",
|
||||
"os_version": "2.3.6",
|
||||
"software_brand": "pretixdroid",
|
||||
"software_version": "4.1.0",
|
||||
"info": {"arbitrary": "data"}
|
||||
@@ -141,29 +133,9 @@ The response will look like this:
|
||||
"id": 3,
|
||||
"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
|
||||
----------------------
|
||||
|
||||
|
||||
@@ -24,8 +24,6 @@ all_events boolean Whether this de
|
||||
limit_events list List of event slugs this device has access to
|
||||
hardware_brand string Device hardware manufacturer (read-only)
|
||||
hardware_model string Device hardware model (read-only)
|
||||
os_name string Device operating system name (read-only)
|
||||
os_version string Device operating system version (read-only)
|
||||
software_brand string Device software product (read-only)
|
||||
software_version string Device software version (read-only)
|
||||
created datetime Creation time
|
||||
@@ -78,8 +76,6 @@ Device endpoints
|
||||
"security_profile": "full",
|
||||
"hardware_brand": "Zebra",
|
||||
"hardware_model": "TC25",
|
||||
"os_name": "Android",
|
||||
"os_version": "8.1.0",
|
||||
"software_brand": "pretixSCAN",
|
||||
"software_version": "1.5.1"
|
||||
}
|
||||
@@ -127,8 +123,6 @@ Device endpoints
|
||||
"security_profile": "full",
|
||||
"hardware_brand": "Zebra",
|
||||
"hardware_model": "TC25",
|
||||
"os_name": "Android",
|
||||
"os_version": "8.1.0",
|
||||
"software_brand": "pretixSCAN",
|
||||
"software_version": "1.5.1"
|
||||
}
|
||||
@@ -179,8 +173,6 @@ Device endpoints
|
||||
"initialized": null
|
||||
"hardware_brand": null,
|
||||
"hardware_model": null,
|
||||
"os_name": null,
|
||||
"os_version": null,
|
||||
"software_brand": null,
|
||||
"software_version": null
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ The reusable medium resource contains the following public fields:
|
||||
Field Type Description
|
||||
===================================== ========================== =======================================================
|
||||
id integer Internal ID of the medium
|
||||
type string Type of medium, e.g. ``"barcode"``, ``"nfc_uid"`` or ``"nfc_mf0aes"``.
|
||||
type string Type of medium, e.g. ``"barcode"`` or ``"nfc_uid"``.
|
||||
organizer string Organizer slug of the organizer who "owns" this medium.
|
||||
identifier string Unique identifier of the medium. The format depends on the ``type``.
|
||||
active boolean Whether this medium may be used.
|
||||
@@ -37,7 +37,6 @@ Existing media types are:
|
||||
|
||||
- ``barcode``
|
||||
- ``nfc_uid``
|
||||
- ``nfc_mf0aes``
|
||||
|
||||
Endpoints
|
||||
---------
|
||||
|
||||
@@ -18,15 +18,8 @@ subject multi-lingual string The subject of
|
||||
template multi-lingual string The body of the email
|
||||
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
|
||||
[**DEPRECATED**] include_pending boolean If ``true``, the email is sent to pending orders. If ``false``,
|
||||
include_pending boolean If ``true``, the email is sent to pending orders. If ``false``,
|
||||
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.
|
||||
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
|
||||
@@ -44,10 +37,7 @@ send_to string Can be ``"order
|
||||
or ``"both"``.
|
||||
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
|
||||
---------
|
||||
@@ -84,11 +74,7 @@ Endpoints
|
||||
"template": {"en": "Don't forget your tickets, download them at {url}"},
|
||||
"all_products": true,
|
||||
"limit_products": [],
|
||||
"restrict_to_status": [
|
||||
"p",
|
||||
"n__not_pending_approval_and_not_valid_if_pending",
|
||||
"n__valid_if_pending"
|
||||
],
|
||||
"include_pending": false,
|
||||
"send_date": null,
|
||||
"send_offset_days": 1,
|
||||
"send_offset_time": "18:00",
|
||||
@@ -134,11 +120,7 @@ Endpoints
|
||||
"template": {"en": "Don't forget your tickets, download them at {url}"},
|
||||
"all_products": true,
|
||||
"limit_products": [],
|
||||
"restrict_to_status": [
|
||||
"p",
|
||||
"n__not_pending_approval_and_not_valid_if_pending",
|
||||
"n__valid_if_pending"
|
||||
],
|
||||
"include_pending": false,
|
||||
"send_date": null,
|
||||
"send_offset_days": 1,
|
||||
"send_offset_time": "18:00",
|
||||
@@ -175,11 +157,7 @@ Endpoints
|
||||
"template": {"en": "Don't forget your tickets, download them at {url}"},
|
||||
"all_products": true,
|
||||
"limit_products": [],
|
||||
"restrict_to_status": [
|
||||
"p",
|
||||
"n__not_pending_approval_and_not_valid_if_pending",
|
||||
"n__valid_if_pending"
|
||||
],
|
||||
"include_pending": false,
|
||||
"send_date": null,
|
||||
"send_offset_days": 1,
|
||||
"send_offset_time": "18:00",
|
||||
@@ -204,11 +182,7 @@ Endpoints
|
||||
"template": {"en": "Don't forget your tickets, download them at {url}"},
|
||||
"all_products": true,
|
||||
"limit_products": [],
|
||||
"restrict_to_status": [
|
||||
"p",
|
||||
"n__not_pending_approval_and_not_valid_if_pending",
|
||||
"n__valid_if_pending"
|
||||
],
|
||||
"include_pending": false,
|
||||
"send_date": null,
|
||||
"send_offset_days": 1,
|
||||
"send_offset_time": "18:00",
|
||||
@@ -261,11 +235,7 @@ Endpoints
|
||||
"template": {"en": "Don't forget your tickets, download them at {url}"},
|
||||
"all_products": true,
|
||||
"limit_products": [],
|
||||
"restrict_to_status": [
|
||||
"p",
|
||||
"n__not_pending_approval_and_not_valid_if_pending",
|
||||
"n__valid_if_pending"
|
||||
],
|
||||
"include_pending": false,
|
||||
"send_date": null,
|
||||
"send_offset_days": 1,
|
||||
"send_offset_time": "18:00",
|
||||
|
||||
@@ -61,7 +61,7 @@ Backend
|
||||
item_formsets, order_search_filter_q, order_search_forms
|
||||
|
||||
.. automodule:: pretix.base.signals
|
||||
:members: logentry_display, logentry_object_link, requiredaction_display, timeline_events, orderposition_blocked_display, customer_created, customer_signed_in
|
||||
:members: logentry_display, logentry_object_link, requiredaction_display, timeline_events, orderposition_blocked_display
|
||||
|
||||
Vouchers
|
||||
""""""""
|
||||
|
||||
@@ -70,8 +70,6 @@ The provider class
|
||||
|
||||
.. autoattribute:: settings_form_fields
|
||||
|
||||
.. autoattribute:: walletqueries
|
||||
|
||||
.. automethod:: settings_form_clean
|
||||
|
||||
.. automethod:: settings_content_render
|
||||
|
||||
@@ -12,4 +12,3 @@ Developer documentation
|
||||
api/index
|
||||
structure
|
||||
translation/index
|
||||
nfc/index
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
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
|
||||
@@ -1,113 +0,0 @@
|
||||
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
|
||||
@@ -1,10 +0,0 @@
|
||||
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.
|
||||
+3
-3
@@ -30,7 +30,7 @@ dependencies = [
|
||||
"babel",
|
||||
"BeautifulSoup4==4.12.*",
|
||||
"bleach==5.0.*",
|
||||
"celery==5.3.*",
|
||||
"celery==5.2.*",
|
||||
"chardet==5.1.*",
|
||||
"cryptography>=3.4.2",
|
||||
"css-inline==0.8.*",
|
||||
@@ -59,10 +59,10 @@ dependencies = [
|
||||
"dnspython==2.3.*",
|
||||
"drf_ujson2==1.7.*",
|
||||
"geoip2==4.*",
|
||||
"importlib_metadata==6.*", # Polyfill, we can probably drop this once we require Python 3.10+
|
||||
"importlib_metadata==6.6.*", # Polyfill, we can probably drop this once we require Python 3.10+
|
||||
"isoweek",
|
||||
"jsonschema",
|
||||
"kombu==5.3.*",
|
||||
"kombu==5.2.*",
|
||||
"libsass==0.22.*",
|
||||
"lxml",
|
||||
"markdown==3.4.3", # 3.3.5 requires importlib-metadata>=4.4, but django-bootstrap3 requires importlib-metadata<3.
|
||||
|
||||
@@ -19,4 +19,4 @@
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
__version__ = "2023.7.2"
|
||||
__version__ = "4.21.0.dev0"
|
||||
|
||||
@@ -223,7 +223,6 @@ class PretixPosSecurityProfile(AllowListSecurityProfile):
|
||||
('POST', 'api-v1:checkinrpc.redeem'),
|
||||
('GET', 'api-v1:checkinrpc.search'),
|
||||
('POST', 'api-v1:reusablemedium-lookup'),
|
||||
('POST', 'api-v1:reusablemedium-list'),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ class IdempotencyMiddleware:
|
||||
auth_hash = sha1(auth_hash_parts.encode()).hexdigest()
|
||||
idempotency_key = request.headers.get('X-Idempotency-Key', '')
|
||||
|
||||
with transaction.atomic(durable=True):
|
||||
with transaction.atomic():
|
||||
call, created = ApiCall.objects.select_for_update(of=OF_SELF).get_or_create(
|
||||
auth_hash=auth_hash,
|
||||
idempotency_key=idempotency_key,
|
||||
@@ -75,7 +75,7 @@ class IdempotencyMiddleware:
|
||||
|
||||
if created:
|
||||
resp = self.get_response(request)
|
||||
with transaction.atomic(durable=True):
|
||||
with transaction.atomic():
|
||||
if resp.status_code in (409, 429, 500, 503):
|
||||
# This is the exception: These calls are *meant* to be retried!
|
||||
call.delete()
|
||||
|
||||
@@ -728,7 +728,6 @@ class EventSettingsSerializer(SettingsSerializer):
|
||||
'payment_term_minutes',
|
||||
'payment_term_last',
|
||||
'payment_term_expire_automatically',
|
||||
'payment_term_expire_delay_days',
|
||||
'payment_term_accept_late',
|
||||
'payment_explanation',
|
||||
'payment_pending_hidden',
|
||||
@@ -817,10 +816,6 @@ class EventSettingsSerializer(SettingsSerializer):
|
||||
'reusable_media_type_nfc_uid',
|
||||
'reusable_media_type_nfc_uid_autocreate_giftcard',
|
||||
'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 = [
|
||||
# These are read-only since they are currently only settable on organizers, not events
|
||||
@@ -830,10 +825,6 @@ class EventSettingsSerializer(SettingsSerializer):
|
||||
'reusable_media_type_nfc_uid',
|
||||
'reusable_media_type_nfc_uid_autocreate_giftcard',
|
||||
'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):
|
||||
@@ -902,8 +893,6 @@ class DeviceEventSettingsSerializer(EventSettingsSerializer):
|
||||
'name_scheme',
|
||||
'reusable_media_type_barcode',
|
||||
'reusable_media_type_nfc_uid',
|
||||
'reusable_media_type_nfc_mf0aes',
|
||||
'reusable_media_type_nfc_mf0aes_random_uid',
|
||||
'system_question_order',
|
||||
]
|
||||
|
||||
|
||||
@@ -251,8 +251,6 @@ class DeviceSerializer(serializers.ModelSerializer):
|
||||
unique_serial = serializers.CharField(read_only=True)
|
||||
hardware_brand = serializers.CharField(read_only=True)
|
||||
hardware_model = serializers.CharField(read_only=True)
|
||||
os_name = serializers.CharField(read_only=True)
|
||||
os_version = serializers.CharField(read_only=True)
|
||||
software_brand = serializers.CharField(read_only=True)
|
||||
software_version = serializers.CharField(read_only=True)
|
||||
created = serializers.DateTimeField(read_only=True)
|
||||
@@ -265,7 +263,7 @@ class DeviceSerializer(serializers.ModelSerializer):
|
||||
fields = (
|
||||
'device_id', 'unique_serial', 'initialization_token', 'all_events', 'limit_events',
|
||||
'revoked', 'name', 'created', 'initialized', 'hardware_brand', 'hardware_model',
|
||||
'os_name', 'os_version', 'software_brand', 'software_version', 'security_profile'
|
||||
'software_brand', 'software_version', 'security_profile'
|
||||
)
|
||||
|
||||
|
||||
@@ -392,9 +390,6 @@ class OrganizerSettingsSerializer(SettingsSerializer):
|
||||
'reusable_media_type_nfc_uid',
|
||||
'reusable_media_type_nfc_uid_autocreate_giftcard',
|
||||
'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):
|
||||
|
||||
@@ -19,12 +19,8 @@
|
||||
# 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 base64
|
||||
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.functions import Coalesce
|
||||
from django.utils.timezone import now
|
||||
@@ -38,8 +34,6 @@ from pretix.api.auth.device import DeviceTokenAuthentication
|
||||
from pretix.api.views.version import numeric_version
|
||||
from pretix.base.models import CheckinList, Device, SubEvent
|
||||
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__)
|
||||
|
||||
@@ -48,73 +42,17 @@ class InitializationRequestSerializer(serializers.Serializer):
|
||||
token = serializers.CharField(max_length=190)
|
||||
hardware_brand = serializers.CharField(max_length=190)
|
||||
hardware_model = serializers.CharField(max_length=190)
|
||||
os_name = serializers.CharField(max_length=190, required=False, allow_null=True)
|
||||
os_version = serializers.CharField(max_length=190, required=False, allow_null=True)
|
||||
software_brand = serializers.CharField(max_length=190)
|
||||
software_version = serializers.CharField(max_length=190)
|
||||
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):
|
||||
hardware_brand = serializers.CharField(max_length=190)
|
||||
hardware_model = serializers.CharField(max_length=190)
|
||||
os_name = serializers.CharField(max_length=190, required=False, allow_null=True)
|
||||
os_version = serializers.CharField(max_length=190, required=False, allow_null=True)
|
||||
software_brand = serializers.CharField(max_length=190)
|
||||
software_version = serializers.CharField(max_length=190)
|
||||
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):
|
||||
@@ -161,13 +99,9 @@ class InitializeView(APIView):
|
||||
device.initialized = now()
|
||||
device.hardware_brand = serializer.validated_data.get('hardware_brand')
|
||||
device.hardware_model = serializer.validated_data.get('hardware_model')
|
||||
device.os_name = serializer.validated_data.get('os_name')
|
||||
device.os_version = serializer.validated_data.get('os_version')
|
||||
device.software_brand = serializer.validated_data.get('software_brand')
|
||||
device.software_version = serializer.validated_data.get('software_version')
|
||||
device.info = serializer.validated_data.get('info')
|
||||
print(serializer.validated_data, request.data)
|
||||
device.rsa_pubkey = serializer.validated_data.get('rsa_pubkey')
|
||||
device.api_token = generate_api_token()
|
||||
device.save()
|
||||
|
||||
@@ -186,15 +120,8 @@ class UpdateView(APIView):
|
||||
device = request.auth
|
||||
device.hardware_brand = serializer.validated_data.get('hardware_brand')
|
||||
device.hardware_model = serializer.validated_data.get('hardware_model')
|
||||
device.os_name = serializer.validated_data.get('os_name')
|
||||
device.os_version = serializer.validated_data.get('os_version')
|
||||
device.software_brand = serializer.validated_data.get('software_brand')
|
||||
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.save()
|
||||
device.log_action('pretix.device.updated', data=serializer.validated_data, auth=device)
|
||||
@@ -242,12 +169,8 @@ class InfoView(APIView):
|
||||
'pretix': __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,12 +104,6 @@ class ReusableMediaViewSet(viewsets.ModelViewSet):
|
||||
auth=self.request.auth,
|
||||
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()
|
||||
def perform_update(self, serializer):
|
||||
|
||||
@@ -26,7 +26,6 @@ from decimal import Decimal
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
import django_filters
|
||||
from django.conf import settings
|
||||
from django.db import transaction
|
||||
from django.db.models import (
|
||||
Exists, F, OuterRef, Prefetch, Q, Subquery, prefetch_related_objects,
|
||||
@@ -946,7 +945,6 @@ with scopes_disabled():
|
||||
| Q(addon_to__attendee_email__icontains=value)
|
||||
| Q(order__code__istartswith=value)
|
||||
| Q(order__invoice_address__name_cached__icontains=value)
|
||||
| Q(order__invoice_address__company__icontains=value)
|
||||
| Q(order__email__icontains=value)
|
||||
| Q(pk__in=matching_media)
|
||||
)
|
||||
@@ -1192,7 +1190,7 @@ class OrderPositionViewSet(viewsets.ModelViewSet):
|
||||
ftype, ignored = mimetypes.guess_type(image_file.name)
|
||||
extension = os.path.basename(image_file.name).split('.')[-1]
|
||||
else:
|
||||
img = Image.open(image_file, formats=settings.PILLOW_FORMATS_QUESTIONS_IMAGE)
|
||||
img = Image.open(image_file)
|
||||
ftype = Image.MIME[img.format]
|
||||
extensions = {
|
||||
'GIF': 'gif', 'TIFF': 'tif', 'BMP': 'bmp', 'JPEG': 'jpg', 'PNG': 'png'
|
||||
|
||||
@@ -140,7 +140,7 @@ class BaseExporter:
|
||||
"""
|
||||
return {}
|
||||
|
||||
def render(self, form_data: dict) -> Tuple[str, str, Optional[bytes]]:
|
||||
def render(self, form_data: dict) -> Tuple[str, str, bytes]:
|
||||
"""
|
||||
Render the exported file and return a tuple consisting of a filename, a file type
|
||||
and file content.
|
||||
|
||||
@@ -500,14 +500,14 @@ class PortraitImageField(SizeValidationMixin, ExtValidationMixin, forms.FileFiel
|
||||
file = BytesIO(data['content'])
|
||||
|
||||
try:
|
||||
image = Image.open(file, formats=settings.PILLOW_FORMATS_QUESTIONS_IMAGE)
|
||||
image = Image.open(file)
|
||||
# verify() must be called immediately after the constructor.
|
||||
image.verify()
|
||||
|
||||
# We want to do more than just verify(), so we need to re-open the file
|
||||
if hasattr(file, 'seek'):
|
||||
file.seek(0)
|
||||
image = Image.open(file, formats=settings.PILLOW_FORMATS_QUESTIONS_IMAGE)
|
||||
image = Image.open(file)
|
||||
|
||||
# load() is a potential DoS vector (see Django bug #18520), so we verify the size first
|
||||
if image.width > 10_000 or image.height > 10_000:
|
||||
@@ -566,7 +566,7 @@ class PortraitImageField(SizeValidationMixin, ExtValidationMixin, forms.FileFiel
|
||||
return f
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs.setdefault('ext_whitelist', settings.FILE_UPLOAD_EXTENSIONS_QUESTION_IMAGE)
|
||||
kwargs.setdefault('ext_whitelist', (".png", ".jpg", ".jpeg", ".jfif", ".tif", ".tiff", ".bmp"))
|
||||
kwargs.setdefault('max_size', settings.FILE_UPLOAD_MAX_SIZE_IMAGE)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@@ -826,7 +826,11 @@ class BaseQuestionsForm(forms.Form):
|
||||
help_text=help_text,
|
||||
initial=initial.file if initial else None,
|
||||
widget=UploadedFileWidget(position=pos, event=event, answer=initial),
|
||||
ext_whitelist=settings.FILE_UPLOAD_EXTENSIONS_OTHER,
|
||||
ext_whitelist=(
|
||||
".png", ".jpg", ".gif", ".jpeg", ".pdf", ".txt", ".docx", ".gif", ".svg",
|
||||
".pptx", ".ppt", ".doc", ".xlsx", ".xls", ".jfif", ".heic", ".heif", ".pages",
|
||||
".bmp", ".tif", ".tiff"
|
||||
),
|
||||
max_size=settings.FILE_UPLOAD_MAX_SIZE_OTHER,
|
||||
)
|
||||
elif q.type == Question.TYPE_DATE:
|
||||
|
||||
@@ -49,9 +49,6 @@ class BaseMediaType:
|
||||
def handle_unknown(self, organizer, identifier, user, auth):
|
||||
pass
|
||||
|
||||
def handle_new(self, organizer, medium, user, auth):
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
return str(self.verbose_name)
|
||||
|
||||
@@ -111,43 +108,9 @@ class NfcUidMediaType(BaseMediaType):
|
||||
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 = {
|
||||
m.identifier: m for m in [
|
||||
BarcodePlainMediaType(),
|
||||
NfcUidMediaType(),
|
||||
NfcMf0aesMediaType(),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
|
||||
from django.conf import settings
|
||||
from django.http import Http404, HttpRequest, HttpResponse
|
||||
from django.middleware.common import CommonMiddleware
|
||||
from django.urls import get_script_prefix, resolve
|
||||
from django.urls import get_script_prefix
|
||||
from django.utils import timezone, translation
|
||||
from django.utils.cache import patch_vary_headers
|
||||
from django.utils.deprecation import MiddlewareMixin
|
||||
@@ -230,8 +230,6 @@ class SecurityMiddleware(MiddlewareMixin):
|
||||
)
|
||||
|
||||
def process_response(self, request, resp):
|
||||
url = resolve(request.path_info)
|
||||
|
||||
if settings.DEBUG and resp.status_code >= 400:
|
||||
# Don't use CSP on debug error page as it breaks of Django's fancy error
|
||||
# pages
|
||||
@@ -251,26 +249,20 @@ class SecurityMiddleware(MiddlewareMixin):
|
||||
|
||||
h = {
|
||||
'default-src': ["{static}"],
|
||||
'script-src': ['{static}'],
|
||||
'script-src': ['{static}', 'https://checkout.stripe.com', 'https://js.stripe.com'],
|
||||
'object-src': ["'none'"],
|
||||
'frame-src': ['{static}'],
|
||||
'frame-src': ['{static}', 'https://checkout.stripe.com', 'https://js.stripe.com'],
|
||||
'style-src': ["{static}", "{media}"],
|
||||
'connect-src': ["{dynamic}", "{media}"],
|
||||
'img-src': ["{static}", "{media}", "data:"] + img_src,
|
||||
'connect-src': ["{dynamic}", "{media}", "https://checkout.stripe.com"],
|
||||
'img-src': ["{static}", "{media}", "data:", "https://*.stripe.com"] + img_src,
|
||||
'font-src': ["{static}"],
|
||||
'media-src': ["{static}", "data:"],
|
||||
# form-action is not only used to match on form actions, but also on URLs
|
||||
# form-actions redirect to. In the context of e.g. payment providers or
|
||||
# single-sign-on this can be nearly anything, so we cannot really restrict
|
||||
# single-sign-on this can be nearly anything so we cannot really restrict
|
||||
# this. However, we'll restrict it to HTTPS.
|
||||
'form-action': ["{dynamic}", "https:"] + (['http:'] if settings.SITE_URL.startswith('http://') else []),
|
||||
}
|
||||
# Only include pay.google.com for wallet detection purposes on the Payment selection page
|
||||
if (
|
||||
url.url_name == "event.order.pay.change" or
|
||||
(url.url_name == "event.checkout" and url.kwargs['step'] == "payment")
|
||||
):
|
||||
h['script-src'].append('https://pay.google.com')
|
||||
if settings.LOG_CSP:
|
||||
h['report-uri'] = ["/csp_report/"]
|
||||
if 'Content-Security-Policy' in resp:
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
# Generated by Django 4.1.9 on 2023-06-26 10:59
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pretixbase', '0242_auto_20230512_1008'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='device',
|
||||
name='os_name',
|
||||
field=models.CharField(max_length=190, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='device',
|
||||
name='os_version',
|
||||
field=models.CharField(max_length=190, null=True),
|
||||
),
|
||||
]
|
||||
@@ -1,35 +0,0 @@
|
||||
# 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'),
|
||||
),
|
||||
]
|
||||
@@ -143,14 +143,6 @@ class Device(LoggedModel):
|
||||
max_length=190,
|
||||
null=True, blank=True
|
||||
)
|
||||
os_name = models.CharField(
|
||||
max_length=190,
|
||||
null=True, blank=True
|
||||
)
|
||||
os_version = models.CharField(
|
||||
max_length=190,
|
||||
null=True, blank=True
|
||||
)
|
||||
software_brand = models.CharField(
|
||||
max_length=190,
|
||||
null=True, blank=True
|
||||
@@ -166,10 +158,6 @@ class Device(LoggedModel):
|
||||
null=True,
|
||||
blank=False
|
||||
)
|
||||
rsa_pubkey = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
info = models.JSONField(
|
||||
null=True, blank=True,
|
||||
)
|
||||
|
||||
@@ -1277,9 +1277,6 @@ class Event(EventMixin, LoggedModel):
|
||||
return not self.orders.exists() and not self.invoices.exists()
|
||||
|
||||
def delete_sub_objects(self):
|
||||
from .checkin import Checkin
|
||||
|
||||
Checkin.all.filter(successful=False, list__event=self).delete()
|
||||
self.cartposition_set.filter(addon_to__isnull=False).delete()
|
||||
self.cartposition_set.all().delete()
|
||||
self.vouchers.all().delete()
|
||||
|
||||
@@ -123,25 +123,3 @@ class ReusableMedium(LoggedModel):
|
||||
unique_together = (("identifier", "type", "organizer"),)
|
||||
index_together = (("identifier", "type", "organizer"), ("updated", "id"))
|
||||
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",
|
||||
),
|
||||
]
|
||||
|
||||
@@ -896,33 +896,6 @@ class Order(LockModel, LoggedModel):
|
||||
), tz)
|
||||
return term_last
|
||||
|
||||
@property
|
||||
def payment_term_expire_date(self):
|
||||
delay = self.event.settings.get('payment_term_expire_delay_days', as_type=int)
|
||||
if not delay: # performance saver + backwards compatibility
|
||||
return self.expires
|
||||
|
||||
term_last = self.payment_term_last
|
||||
if term_last and self.expires > term_last: # backwards compatibility
|
||||
return self.expires
|
||||
|
||||
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)
|
||||
expires = make_aware(datetime.combine(
|
||||
expires,
|
||||
time(hour=23, minute=59, second=59)
|
||||
), tz)
|
||||
if term_last:
|
||||
return min(expires, term_last)
|
||||
else:
|
||||
return expires
|
||||
|
||||
def _can_be_paid(self, count_waitinglist=True, ignore_date=False, force=False) -> Union[bool, str]:
|
||||
error_messages = {
|
||||
'late_lastdate': _("The payment can not be accepted as the last date of payments configured in the "
|
||||
@@ -1246,7 +1219,7 @@ class QuestionAnswer(models.Model):
|
||||
|
||||
@property
|
||||
def is_image(self):
|
||||
return any(self.file.name.lower().endswith(e) for e in settings.FILE_UPLOAD_EXTENSIONS_QUESTION_IMAGE)
|
||||
return any(self.file.name.lower().endswith(e) for e in ('.jpg', '.png', '.gif', '.tiff', '.bmp', '.jpeg'))
|
||||
|
||||
@property
|
||||
def file_name(self):
|
||||
@@ -1672,13 +1645,12 @@ class OrderPayment(models.Model):
|
||||
if status_change:
|
||||
self.order.create_transactions()
|
||||
|
||||
def fail(self, info=None, user=None, auth=None, log_data=None, send_mail=True):
|
||||
def fail(self, info=None, user=None, auth=None, log_data=None):
|
||||
"""
|
||||
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,
|
||||
but it adds strong database logging since we do not want to report a failure for an order that has just
|
||||
been marked as paid.
|
||||
:param send_mail: Whether an email should be sent to the user about this event (default: ``True``).
|
||||
"""
|
||||
with transaction.atomic():
|
||||
locked_instance = OrderPayment.objects.select_for_update(of=OF_SELF).get(pk=self.pk)
|
||||
@@ -1703,17 +1675,6 @@ class OrderPayment(models.Model):
|
||||
'info': info,
|
||||
'data': log_data,
|
||||
}, 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
|
||||
|
||||
def confirm(self, count_waitinglist=True, send_mail=True, force=False, user=None, auth=None, mail_text='',
|
||||
|
||||
@@ -28,7 +28,6 @@ import pycountry
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.validators import EmailValidator
|
||||
from django.db.models import Q
|
||||
from django.utils import formats
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.translation import (
|
||||
@@ -43,8 +42,8 @@ from phonenumbers import SUPPORTED_REGIONS
|
||||
from pretix.base.channels import get_all_sales_channels
|
||||
from pretix.base.forms.questions import guess_country
|
||||
from pretix.base.models import (
|
||||
Customer, ItemVariation, OrderPosition, Question, QuestionAnswer,
|
||||
QuestionOption, Seat, SubEvent,
|
||||
ItemVariation, OrderPosition, Question, QuestionAnswer, QuestionOption,
|
||||
Seat, SubEvent,
|
||||
)
|
||||
from pretix.base.services.pricing import get_price
|
||||
from pretix.base.settings import (
|
||||
@@ -827,28 +826,6 @@ class QuestionColumn(ImportColumn):
|
||||
a.options.add(*a._options)
|
||||
|
||||
|
||||
class CustomerColumn(ImportColumn):
|
||||
identifier = 'customer'
|
||||
verbose_name = gettext_lazy('Customer')
|
||||
|
||||
def clean(self, value, previous_values):
|
||||
if value:
|
||||
try:
|
||||
value = self.event.organizer.customers.get(
|
||||
Q(identifier=value) | Q(email__iexact=value) | Q(external_identifier=value)
|
||||
)
|
||||
except Customer.MultipleObjectsReturned:
|
||||
value = self.event.organizer.customers.get(
|
||||
Q(identifier=value)
|
||||
)
|
||||
except Customer.DoesNotExist:
|
||||
raise ValidationError(_('No matching customer was found.'))
|
||||
return value
|
||||
|
||||
def assign(self, value, order, position, invoice_address, **kwargs):
|
||||
order.customer = value
|
||||
|
||||
|
||||
def get_all_columns(event):
|
||||
default = []
|
||||
if event.has_subevents:
|
||||
@@ -860,10 +837,6 @@ def get_all_columns(event):
|
||||
Variation(event),
|
||||
InvoiceAddressCompany(event),
|
||||
]
|
||||
if event.settings.customer_accounts:
|
||||
default += [
|
||||
CustomerColumn(event),
|
||||
]
|
||||
scheme = PERSON_NAME_SCHEMES.get(event.settings.name_scheme)
|
||||
for n, l, w in scheme['fields']:
|
||||
default.append(InvoiceAddressNamePart(event, n, l))
|
||||
|
||||
@@ -60,7 +60,7 @@ from pretix.base.channels import get_all_sales_channels
|
||||
from pretix.base.forms import PlaceholderValidator
|
||||
from pretix.base.models import (
|
||||
CartPosition, Event, GiftCard, InvoiceAddress, Order, OrderPayment,
|
||||
OrderRefund, Quota, TaxRule,
|
||||
OrderRefund, Quota,
|
||||
)
|
||||
from pretix.base.reldate import RelativeDateField, RelativeDateWrapper
|
||||
from pretix.base.settings import SettingsSandbox
|
||||
@@ -78,16 +78,6 @@ from pretix.presale.views.cart import cart_session, get_or_create_cart_id
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class WalletQueries:
|
||||
APPLEPAY = 'applepay'
|
||||
GOOGLEPAY = 'googlepay'
|
||||
|
||||
WALLETS = (
|
||||
(APPLEPAY, pgettext_lazy('payment', 'Apple Pay')),
|
||||
(GOOGLEPAY, pgettext_lazy('payment', 'Google Pay')),
|
||||
)
|
||||
|
||||
|
||||
class PaymentProviderForm(Form):
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
@@ -446,19 +436,6 @@ class BasePaymentProvider:
|
||||
d['_restrict_to_sales_channels']._as_type = list
|
||||
return d
|
||||
|
||||
@property
|
||||
def walletqueries(self):
|
||||
"""
|
||||
.. warning:: This property is considered **experimental**. It might change or get removed at any time without
|
||||
prior notice.
|
||||
|
||||
A list of wallet payment methods that should be dynamically joined to the public name of the payment method,
|
||||
if they are available to the user.
|
||||
The detection is made on a best effort basis with no guarantees of correctness and actual availability.
|
||||
Wallets that pretix can check for are exposed through ``pretix.base.payment.WalletQueries``.
|
||||
"""
|
||||
return []
|
||||
|
||||
def settings_form_clean(self, cleaned_data):
|
||||
"""
|
||||
Overriding this method allows you to inject custom validation into the settings form.
|
||||
@@ -1038,11 +1015,7 @@ class FreeOrderProvider(BasePaymentProvider):
|
||||
|
||||
cart = get_cart(request)
|
||||
total = get_cart_total(request)
|
||||
try:
|
||||
total += sum([f.value for f in get_fees(self.event, request, total, None, None, cart)])
|
||||
except TaxRule.SaleNotAllowed:
|
||||
# ignore for now, will fail on order creation
|
||||
pass
|
||||
total += sum([f.value for f in get_fees(self.event, request, total, None, None, cart)])
|
||||
return total == 0
|
||||
|
||||
def order_change_allowed(self, order: Order) -> bool:
|
||||
|
||||
+28
-119
@@ -43,18 +43,16 @@ import subprocess
|
||||
import tempfile
|
||||
import unicodedata
|
||||
import uuid
|
||||
from collections import OrderedDict, defaultdict
|
||||
from collections import OrderedDict
|
||||
from functools import partial
|
||||
from io import BytesIO
|
||||
|
||||
import jsonschema
|
||||
import reportlab.rl_config
|
||||
from bidi.algorithm import get_display
|
||||
from django.conf import settings
|
||||
from django.contrib.staticfiles import finders
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db.models import Max, Min
|
||||
from django.db.models.fields.files import FieldFile
|
||||
from django.dispatch import receiver
|
||||
from django.utils.deconstruct import deconstructible
|
||||
from django.utils.formats import date_format
|
||||
@@ -62,8 +60,7 @@ from django.utils.html import conditional_escape
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext_lazy as _, pgettext
|
||||
from i18nfield.strings import LazyI18nString
|
||||
from pypdf import PdfReader, PdfWriter, Transformation
|
||||
from pypdf.generic import RectangleObject
|
||||
from pypdf import PdfReader
|
||||
from reportlab.graphics import renderPDF
|
||||
from reportlab.graphics.barcode.qr import QrCodeWidget
|
||||
from reportlab.graphics.shapes import Drawing
|
||||
@@ -88,9 +85,6 @@ from pretix.presale.style import get_fonts
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
if not settings.DEBUG:
|
||||
reportlab.rl_config.shapeChecking = 0
|
||||
|
||||
|
||||
DEFAULT_VARIABLES = OrderedDict((
|
||||
("secret", {
|
||||
@@ -361,9 +355,14 @@ DEFAULT_VARIABLES = OrderedDict((
|
||||
}),
|
||||
("addons", {
|
||||
"label": _("List of Add-Ons"),
|
||||
"editor_sample": _("Add-on 1\n2x Add-on 2"),
|
||||
"editor_sample": _("Add-on 1\nAdd-on 2"),
|
||||
"evaluate": lambda op, order, ev: "\n".join([
|
||||
str(p) for p in generate_compressed_addon_list(op, order, ev)
|
||||
'{} - {}'.format(p.item.name, p.variation.value) if p.variation else str(p.item.name)
|
||||
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", {
|
||||
@@ -521,7 +520,7 @@ def images_from_questions(sender, *args, **kwargs):
|
||||
else:
|
||||
a = op.answers.filter(question_id=question_id).first() or a
|
||||
|
||||
if not a or not a.file or not any(a.file.name.lower().endswith(e) for e in settings.FILE_UPLOAD_EXTENSIONS_QUESTION_IMAGE):
|
||||
if not a or not a.file or not any(a.file.name.lower().endswith(e) for e in (".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tif", ".tiff")):
|
||||
return None
|
||||
else:
|
||||
if etag:
|
||||
@@ -697,30 +696,6 @@ def get_seat(op: OrderPosition):
|
||||
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:
|
||||
|
||||
def __init__(self, event, layout, background_file):
|
||||
@@ -885,37 +860,22 @@ class Renderer:
|
||||
image_file = None
|
||||
|
||||
if image_file:
|
||||
ir = ThumbnailingImageReader(image_file)
|
||||
try:
|
||||
ir = ThumbnailingImageReader(image_file)
|
||||
ir.resize(float(o['width']) * mm, float(o['height']) * mm, 300)
|
||||
canvas.drawImage(
|
||||
image=ir,
|
||||
x=float(o['left']) * mm,
|
||||
y=float(o['bottom']) * mm,
|
||||
width=float(o['width']) * mm,
|
||||
height=float(o['height']) * mm,
|
||||
preserveAspectRatio=True,
|
||||
anchor='c', # centered in frame
|
||||
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:
|
||||
logger.exception("Can not load or resize image")
|
||||
canvas.saveState()
|
||||
canvas.setFillColorRGB(.8, .8, .8, alpha=1)
|
||||
canvas.rect(
|
||||
x=float(o['left']) * mm,
|
||||
y=float(o['bottom']) * mm,
|
||||
width=float(o['width']) * mm,
|
||||
height=float(o['height']) * mm,
|
||||
stroke=0,
|
||||
fill=1,
|
||||
)
|
||||
canvas.restoreState()
|
||||
logger.exception("Can not resize image")
|
||||
pass
|
||||
canvas.drawImage(
|
||||
image=ir,
|
||||
x=float(o['left']) * mm,
|
||||
y=float(o['bottom']) * mm,
|
||||
width=float(o['width']) * mm,
|
||||
height=float(o['height']) * mm,
|
||||
preserveAspectRatio=True,
|
||||
anchor='c', # centered in frame
|
||||
mask='auto'
|
||||
)
|
||||
else:
|
||||
canvas.saveState()
|
||||
canvas.setFillColorRGB(.8, .8, .8, alpha=1)
|
||||
@@ -969,7 +929,7 @@ class Renderer:
|
||||
|
||||
# reportlab does not support unicode combination characters
|
||||
# It's important we do this before we use ArabicReshaper
|
||||
text = unicodedata.normalize("NFC", text)
|
||||
text = unicodedata.normalize("NFKC", text)
|
||||
|
||||
# reportlab does not support RTL, ligature-heavy scripts like Arabic. Therefore, we use ArabicReshaper
|
||||
# to resolve all ligatures and python-bidi to switch RTL texts.
|
||||
@@ -1022,10 +982,7 @@ class Renderer:
|
||||
elif o['type'] == "poweredby":
|
||||
self._draw_poweredby(canvas, op, o)
|
||||
if self.bg_pdf:
|
||||
page_size = (
|
||||
self.bg_pdf.pages[0].mediabox[2] - self.bg_pdf.pages[0].mediabox[0],
|
||||
self.bg_pdf.pages[0].mediabox[3] - self.bg_pdf.pages[0].mediabox[1]
|
||||
)
|
||||
page_size = (self.bg_pdf.pages[0].mediabox[2], self.bg_pdf.pages[0].mediabox[3])
|
||||
if self.bg_pdf.pages[0].get('/Rotate') in (90, 270):
|
||||
# swap dimensions due to pdf being rotated
|
||||
page_size = page_size[::-1]
|
||||
@@ -1053,12 +1010,14 @@ class Renderer:
|
||||
with open(os.path.join(d, 'out.pdf'), 'rb') as f:
|
||||
return BytesIO(f.read())
|
||||
else:
|
||||
from pypdf import PdfReader, PdfWriter, Transformation
|
||||
from pypdf.generic import RectangleObject
|
||||
buffer.seek(0)
|
||||
new_pdf = PdfReader(buffer)
|
||||
output = PdfWriter()
|
||||
|
||||
for i, page in enumerate(new_pdf.pages):
|
||||
bg_page = copy.deepcopy(self.bg_pdf.pages[i])
|
||||
bg_page = copy.copy(self.bg_pdf.pages[i])
|
||||
bg_rotation = bg_page.get('/Rotate')
|
||||
if bg_rotation:
|
||||
# /Rotate is clockwise, transformation.rotate is counter-clockwise
|
||||
@@ -1095,56 +1054,6 @@ class Renderer:
|
||||
return outbuffer
|
||||
|
||||
|
||||
def merge_background(fg_pdf, bg_pdf, out_file, compress):
|
||||
if settings.PDFTK:
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
fg_filename = os.path.join(d, 'fg.pdf')
|
||||
bg_filename = os.path.join(d, 'bg.pdf')
|
||||
fg_pdf.write(fg_filename)
|
||||
bg_pdf.write(bg_filename)
|
||||
pdftk_cmd = [
|
||||
settings.PDFTK,
|
||||
fg_filename,
|
||||
'multibackground',
|
||||
bg_filename,
|
||||
'output',
|
||||
'-',
|
||||
]
|
||||
if compress:
|
||||
pdftk_cmd.append('compress')
|
||||
subprocess.run(pdftk_cmd, check=True, stdout=out_file)
|
||||
else:
|
||||
output = PdfWriter()
|
||||
for i, page in enumerate(fg_pdf.pages):
|
||||
bg_page = copy.deepcopy(bg_pdf.pages[i])
|
||||
bg_rotation = bg_page.get('/Rotate')
|
||||
if bg_rotation:
|
||||
# /Rotate is clockwise, transformation.rotate is counter-clockwise
|
||||
t = Transformation().rotate(bg_rotation)
|
||||
w = float(page.mediabox.getWidth())
|
||||
h = float(page.mediabox.getHeight())
|
||||
if bg_rotation in (90, 270):
|
||||
# offset due to rotation base
|
||||
if bg_rotation == 90:
|
||||
t = t.translate(h, 0)
|
||||
else:
|
||||
t = t.translate(0, w)
|
||||
# rotate mediabox as well
|
||||
page.mediabox = RectangleObject((
|
||||
page.mediabox.left.as_numeric(),
|
||||
page.mediabox.bottom.as_numeric(),
|
||||
page.mediabox.top.as_numeric(),
|
||||
page.mediabox.right.as_numeric(),
|
||||
))
|
||||
page.trimbox = page.mediabox
|
||||
elif bg_rotation == 180:
|
||||
t = t.translate(w, h)
|
||||
page.add_transformation(t)
|
||||
bg_page.merge_page(page)
|
||||
output.add_page(bg_page)
|
||||
output.write(out_file)
|
||||
|
||||
|
||||
@deconstructible
|
||||
class PdfLayoutValidator:
|
||||
def __call__(self, value):
|
||||
|
||||
@@ -141,10 +141,9 @@ error_messages = {
|
||||
'price_not_a_number': gettext_lazy('The entered price is not a number.'),
|
||||
'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_min_usages': 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.',
|
||||
'number'
|
||||
'voucher_min_usages': gettext_lazy(
|
||||
'The voucher code "%(voucher)s" can only be used if you select at least %(number)s '
|
||||
'matching products.'
|
||||
),
|
||||
'voucher_min_usages_removed': ngettext_lazy(
|
||||
'The voucher code "%(voucher)s" can only be used if you select at least %(number)s matching products. '
|
||||
@@ -318,9 +317,6 @@ class CartManager:
|
||||
def _delete_out_of_timeframe(self):
|
||||
err = None
|
||||
for cp in self.positions:
|
||||
if not cp.pk:
|
||||
continue
|
||||
|
||||
if cp.subevent and cp.subevent.presale_start and self.now_dt < cp.subevent.presale_start:
|
||||
err = error_messages['some_subevent_not_started']
|
||||
cp.addons.all().delete()
|
||||
|
||||
@@ -86,8 +86,8 @@ def _build_time(t=None, value=None, ev=None, now_dt=None):
|
||||
return ev.date_admission or ev.date_from
|
||||
|
||||
|
||||
def _logic_annotate_for_graphic_explain(rules, ev, rule_data, now_dt):
|
||||
logic_environment = _get_logic_environment(ev, now_dt)
|
||||
def _logic_annotate_for_graphic_explain(rules, ev, rule_data):
|
||||
logic_environment = _get_logic_environment(ev)
|
||||
event = ev if isinstance(ev, Event) else ev.event
|
||||
|
||||
def _evaluate_inners(r):
|
||||
@@ -152,7 +152,7 @@ def _logic_explain(rules, ev, rule_data, now_dt=None):
|
||||
get in before 17:00". In the middle of the night it would switch to "You can only get in after 09:00".
|
||||
"""
|
||||
now_dt = now_dt or now()
|
||||
logic_environment = _get_logic_environment(ev, now_dt)
|
||||
logic_environment = _get_logic_environment(ev)
|
||||
_var_values = {'False': False, 'True': True}
|
||||
_var_explanations = {}
|
||||
|
||||
@@ -229,7 +229,7 @@ def _logic_explain(rules, ev, rule_data, now_dt=None):
|
||||
for vname, data in _var_explanations.items():
|
||||
var, operator, rhs = data['var'], data['operator'], data['rhs']
|
||||
if var == 'now':
|
||||
compare_to = _build_time(*rhs[0]['buildTime'], ev=ev, now_dt=now_dt).astimezone(ev.timezone)
|
||||
compare_to = _build_time(*rhs[0]['buildTime'], ev=ev).astimezone(ev.timezone)
|
||||
tolerance = timedelta(minutes=float(rhs[1])) if len(rhs) > 1 and rhs[1] else timedelta(seconds=0)
|
||||
if operator == 'isBefore':
|
||||
compare_to += tolerance
|
||||
@@ -337,7 +337,7 @@ def _logic_explain(rules, ev, rule_data, now_dt=None):
|
||||
return ', '.join(var_texts[v] for v in paths_with_min_weight[0] if not _var_values[v])
|
||||
|
||||
|
||||
def _get_logic_environment(ev, now_dt):
|
||||
def _get_logic_environment(ev):
|
||||
# Every change to our supported JSON logic must be done
|
||||
# * in pretix.base.services.checkin
|
||||
# * in pretix.base.models.checkin
|
||||
@@ -354,7 +354,7 @@ def _get_logic_environment(ev, now_dt):
|
||||
logic.add_operation('objectList', lambda *objs: list(objs))
|
||||
logic.add_operation('lookup', lambda model, pk, str: int(pk))
|
||||
logic.add_operation('inList', lambda a, b: a in b)
|
||||
logic.add_operation('buildTime', partial(_build_time, ev=ev, now_dt=now_dt))
|
||||
logic.add_operation('buildTime', partial(_build_time, ev=ev))
|
||||
logic.add_operation('isBefore', is_before)
|
||||
logic.add_operation('isAfter', lambda t1, t2, tol=None: is_before(t2, t1, tol))
|
||||
return logic
|
||||
@@ -861,7 +861,7 @@ def perform_checkin(op: OrderPosition, clist: CheckinList, given_answers: dict,
|
||||
|
||||
if type == Checkin.TYPE_ENTRY and clist.rules:
|
||||
rule_data = LazyRuleVars(op, clist, dt)
|
||||
logic = _get_logic_environment(op.subevent or clist.event, now_dt=dt)
|
||||
logic = _get_logic_environment(op.subevent or clist.event)
|
||||
if not logic.apply(clist.rules, rule_data):
|
||||
if force:
|
||||
force_used = True
|
||||
|
||||
@@ -26,7 +26,7 @@ from typing import Any, Dict, Union
|
||||
from celery.exceptions import MaxRetriesExceededError
|
||||
from django.conf import settings
|
||||
from django.core.files.base import ContentFile
|
||||
from django.db import close_old_connections, connection, transaction
|
||||
from django.db import connection, transaction
|
||||
from django.dispatch import receiver
|
||||
from django.utils.timezone import now, override
|
||||
from django.utils.translation import gettext
|
||||
@@ -86,12 +86,9 @@ def export(self, event: Event, fileid: str, provider: str, form_data: Dict[str,
|
||||
gettext('Your export did not contain any data.')
|
||||
)
|
||||
file.filename, file.type, data = d
|
||||
|
||||
close_old_connections() # This task can run very long, we might need a new DB connection
|
||||
|
||||
f = ContentFile(data)
|
||||
file.file.save(cachedfile_name(file, file.filename), f)
|
||||
return str(file.pk)
|
||||
return file.pk
|
||||
|
||||
|
||||
@app.task(base=ProfiledOrganizerUserTask, throws=(ExportError,), bind=True)
|
||||
@@ -157,12 +154,9 @@ def multiexport(self, organizer: Organizer, user: User, device: int, token: int,
|
||||
gettext('Your export did not contain any data.')
|
||||
)
|
||||
file.filename, file.type, data = d
|
||||
|
||||
close_old_connections() # This task can run very long, we might need a new DB connection
|
||||
|
||||
f = ContentFile(data)
|
||||
file.file.save(cachedfile_name(file, file.filename), f)
|
||||
return str(file.pk)
|
||||
return file.pk
|
||||
|
||||
|
||||
def _run_scheduled_export(schedule, context: Union[Event, Organizer], exporter, config_url, retry_func, has_permission):
|
||||
@@ -220,11 +214,6 @@ def _run_scheduled_export(schedule, context: Union[Event, Organizer], exporter,
|
||||
raise ExportError(
|
||||
gettext('Your exported data exceeded the size limit for scheduled exports.')
|
||||
)
|
||||
|
||||
conn = transaction.get_connection()
|
||||
if not conn.in_atomic_block: # atomic execution only happens during tests or with celery always_eager on
|
||||
close_old_connections() # This task can run very long, we might need a new DB connection
|
||||
|
||||
f = ContentFile(data)
|
||||
file.file.save(cachedfile_name(file, file.filename), f)
|
||||
except ExportEmptyError as e:
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
#
|
||||
# This file is part of pretix (Community Edition).
|
||||
#
|
||||
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||
# Copyright (C) 2020-2021 rami.io GmbH and contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
|
||||
# Public License as published by the Free Software Foundation in version 3 of the License.
|
||||
#
|
||||
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
|
||||
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
|
||||
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
|
||||
# this file, see <https://pretix.eu/about/en/license>.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
import 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
|
||||
@@ -935,10 +935,7 @@ def _create_order(event: Event, email: str, positions: List[CartPosition], now_d
|
||||
raise OrderError(e.message)
|
||||
|
||||
require_approval = any(p.requires_approval(invoice_address=address) for p in positions)
|
||||
try:
|
||||
fees = _get_fees(positions, payment_requests, address, meta_info, event, require_approval=require_approval)
|
||||
except TaxRule.SaleNotAllowed:
|
||||
raise OrderError(error_messages['country_blocked'])
|
||||
fees = _get_fees(positions, payment_requests, address, meta_info, event, require_approval=require_approval)
|
||||
total = pending_sum = sum([c.price for c in positions]) + sum([c.value for c in fees])
|
||||
|
||||
order = Order(
|
||||
@@ -971,10 +968,7 @@ def _create_order(event: Event, email: str, positions: List[CartPosition], now_d
|
||||
|
||||
for fee in fees:
|
||||
fee.order = order
|
||||
try:
|
||||
fee._calculate_tax()
|
||||
except TaxRule.SaleNotAllowed:
|
||||
raise OrderError(error_messages['country_blocked'])
|
||||
fee._calculate_tax()
|
||||
if fee.tax_rule and not fee.tax_rule.pk:
|
||||
fee.tax_rule = None # TODO: deprecate
|
||||
fee.save()
|
||||
@@ -1270,12 +1264,12 @@ def expire_orders(sender, **kwargs):
|
||||
Exists(
|
||||
OrderFee.objects.filter(order_id=OuterRef('pk'), fee_type=OrderFee.FEE_TYPE_CANCELLATION)
|
||||
)
|
||||
).prefetch_related('event').order_by('event_id')
|
||||
).select_related('event').order_by('event_id')
|
||||
for o in qs:
|
||||
if o.event_id != event_id:
|
||||
expire = o.event.settings.get('payment_term_expire_automatically', as_type=bool)
|
||||
event_id = o.event_id
|
||||
if expire and now() >= o.payment_term_expire_date:
|
||||
if expire:
|
||||
mark_order_expired(o)
|
||||
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
|
||||
# This file is based on an earlier version of pretix which was released under the Apache License 2.0. The full text of
|
||||
# the Apache License 2.0 can be obtained at <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||
#
|
||||
@@ -32,7 +31,7 @@
|
||||
# Unless required by applicable law or agreed to in writing, software distributed under the Apache License 2.0 is
|
||||
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations under the License.
|
||||
import inspect
|
||||
|
||||
import json
|
||||
from datetime import timedelta
|
||||
from tempfile import NamedTemporaryFile
|
||||
@@ -42,13 +41,10 @@ from zipfile import ZipFile
|
||||
from dateutil.parser import parse
|
||||
from django.conf import settings
|
||||
from django.utils.crypto import get_random_string
|
||||
from django.utils.formats import date_format
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.models import CachedFile, Event, User, cachedfile_name
|
||||
from pretix.base.services.mail import SendMailException, mail
|
||||
from pretix.base.models import CachedFile, Event, cachedfile_name
|
||||
from pretix.base.services.tasks import ProfiledEventTask
|
||||
from pretix.base.shredder import ShredError
|
||||
from pretix.celery_app import app
|
||||
@@ -105,20 +101,8 @@ def export(event: Event, shredders: List[str], session_key=None, cfid=None) -> N
|
||||
return cf.pk
|
||||
|
||||
|
||||
@app.task(base=ProfiledEventTask, throws=(ShredError,), bind=True)
|
||||
def shred(self, event: Event, fileid: str, confirm_code: str, user: int=None, locale: str='en') -> None:
|
||||
steps = []
|
||||
|
||||
if user:
|
||||
user = User.objects.get(pk=user)
|
||||
|
||||
def set_progress(val):
|
||||
if not self.request.called_directly:
|
||||
self.update_state(
|
||||
state='PROGRESS',
|
||||
meta={'value': val, 'steps': steps}
|
||||
)
|
||||
|
||||
@app.task(base=ProfiledEventTask, throws=(ShredError,))
|
||||
def shred(event: Event, fileid: str, confirm_code: str) -> None:
|
||||
known_shredders = event.get_data_shredders()
|
||||
try:
|
||||
cf = CachedFile.objects.get(pk=fileid)
|
||||
@@ -140,52 +124,8 @@ def shred(self, event: Event, fileid: str, confirm_code: str, user: int=None, lo
|
||||
if event.logentry_set.filter(datetime__gte=parse(indexdata['time'])):
|
||||
raise ShredError(_("Something happened in your event after the export, please try again."))
|
||||
|
||||
event.log_action(
|
||||
'pretix.event.shredder.started', user=user, data={
|
||||
'indexdata': indexdata
|
||||
}
|
||||
)
|
||||
|
||||
for i, shredder in enumerate(shredders):
|
||||
with language(locale):
|
||||
steps.append({'label': str(shredder.verbose_name), 'done': False})
|
||||
set_progress(i * 100 / len(shredders))
|
||||
if 'progress_callback' in inspect.signature(shredder.shred_data).parameters:
|
||||
shredder.shred_data(
|
||||
progress_callback=lambda y: set_progress(
|
||||
i * 100 / len(shredders) + min(max(y, 0), 100) / 100 * 100 / len(shredders)
|
||||
)
|
||||
)
|
||||
else:
|
||||
shredder.shred_data()
|
||||
steps[-1]['done'] = True
|
||||
for shredder in shredders:
|
||||
shredder.shred_data()
|
||||
|
||||
cf.file.delete(save=False)
|
||||
cf.delete()
|
||||
|
||||
event.log_action(
|
||||
'pretix.event.shredder.completed', user=user, data={
|
||||
'indexdata': indexdata
|
||||
}
|
||||
)
|
||||
|
||||
if user:
|
||||
with language(user.locale):
|
||||
try:
|
||||
mail(
|
||||
user.email,
|
||||
_('Data shredding completed'),
|
||||
'pretixbase/email/shred_completed.txt',
|
||||
{
|
||||
'user': user,
|
||||
'organizer': event.organizer.name,
|
||||
'event': str(event.name),
|
||||
'start_time': date_format(parse(indexdata['time']).astimezone(event.timezone), 'SHORT_DATETIME_FORMAT'),
|
||||
'shredders': ', '.join([str(s.verbose_name) for s in shredders])
|
||||
},
|
||||
event=None,
|
||||
user=user,
|
||||
locale=user.locale,
|
||||
)
|
||||
except SendMailException:
|
||||
pass # Already logged
|
||||
|
||||
@@ -259,46 +259,6 @@ DEFAULTS = {
|
||||
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': {
|
||||
'default': '10',
|
||||
'type': int,
|
||||
@@ -933,28 +893,6 @@ DEFAULTS = {
|
||||
"the pool and can be ordered by other people."),
|
||||
)
|
||||
},
|
||||
'payment_term_expire_delay_days': {
|
||||
'default': '0',
|
||||
'type': int,
|
||||
'form_class': forms.IntegerField,
|
||||
'serializer_class': serializers.IntegerField,
|
||||
'form_kwargs': dict(
|
||||
label=_('Expiration delay'),
|
||||
help_text=_("The order will only actually expire this many days after the expiration date communicated "
|
||||
"to the customer. If you select \"Only end payment terms on weekdays\" above, this will also "
|
||||
"be respected. However, this will not delay beyond the \"last date of payments\" "
|
||||
"configured above, which is always enforced."),
|
||||
# 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
|
||||
# to be checked.
|
||||
min_value=0,
|
||||
max_value=30,
|
||||
),
|
||||
'serializer_kwargs': dict(
|
||||
min_value=0,
|
||||
max_value=30,
|
||||
),
|
||||
},
|
||||
'payment_pending_hidden': {
|
||||
'default': 'False',
|
||||
'type': bool,
|
||||
@@ -2341,24 +2279,6 @@ missing additional payment of **{pending_sum}**.
|
||||
You can view the payment information and the status of your order at
|
||||
{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,
|
||||
Your {event} team""")) # noqa: W291
|
||||
},
|
||||
@@ -2793,7 +2713,7 @@ Your {organizer} team""")) # noqa: W291
|
||||
'form_class': ExtFileField,
|
||||
'form_kwargs': dict(
|
||||
label=_('Header image'),
|
||||
ext_whitelist=settings.FILE_UPLOAD_EXTENSIONS_IMAGE,
|
||||
ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"),
|
||||
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
|
||||
help_text=_('If you provide a logo image, we will by default not show your event name and date '
|
||||
'in the page header. By default, we show your logo with a size of up to 1140x120 pixels. You '
|
||||
@@ -2836,7 +2756,7 @@ Your {organizer} team""")) # noqa: W291
|
||||
'form_class': ExtFileField,
|
||||
'form_kwargs': dict(
|
||||
label=_('Header image'),
|
||||
ext_whitelist=settings.FILE_UPLOAD_EXTENSIONS_IMAGE,
|
||||
ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"),
|
||||
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
|
||||
help_text=_('If you provide a logo image, we will by default not show your organization name '
|
||||
'in the page header. By default, we show your logo with a size of up to 1140x120 pixels. You '
|
||||
@@ -2876,7 +2796,7 @@ Your {organizer} team""")) # noqa: W291
|
||||
'form_class': ExtFileField,
|
||||
'form_kwargs': dict(
|
||||
label=_('Social media image'),
|
||||
ext_whitelist=settings.FILE_UPLOAD_EXTENSIONS_IMAGE,
|
||||
ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"),
|
||||
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
|
||||
help_text=_('This picture will be used as a preview if you post links to your ticket shop on social media. '
|
||||
'Facebook advises to use a picture size of 1200 x 630 pixels, however some platforms like '
|
||||
@@ -2897,7 +2817,7 @@ Your {organizer} team""")) # noqa: W291
|
||||
'form_class': ExtFileField,
|
||||
'form_kwargs': dict(
|
||||
label=_('Logo image'),
|
||||
ext_whitelist=settings.FILE_UPLOAD_EXTENSIONS_IMAGE,
|
||||
ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"),
|
||||
required=False,
|
||||
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
|
||||
help_text=_('We will show your logo with a maximal height and width of 2.5 cm.')
|
||||
@@ -3715,10 +3635,14 @@ def validate_organizer_settings(organizer, settings_dict):
|
||||
# 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
|
||||
# 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({
|
||||
'reusable_media_type_nfc_uid': _('This needs to be disabled if other NFC-based types are active.')
|
||||
})
|
||||
"""
|
||||
|
||||
|
||||
def global_settings_object(holder):
|
||||
|
||||
+66
-232
@@ -32,12 +32,11 @@
|
||||
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations under the License.
|
||||
|
||||
import copy
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
from typing import List, Tuple
|
||||
|
||||
from django.db import transaction
|
||||
from django.db.models import Max, Q
|
||||
from django.db.models.functions import Greatest
|
||||
from django.dispatch import receiver
|
||||
@@ -100,13 +99,11 @@ class BaseDataShredder:
|
||||
"""
|
||||
raise NotImplementedError() # NOQA
|
||||
|
||||
def shred_data(self, progress_callback=None):
|
||||
def shred_data(self):
|
||||
"""
|
||||
This method is called to actually remove the data from the system. You should remove any database objects
|
||||
here.
|
||||
|
||||
You can call ``progress_callback`` with an integer value between 0 and 100 to communicate back your progress.
|
||||
|
||||
You should never delete ``LogEntry`` objects, but you might modify them to remove personal data. In this
|
||||
case, set the ``LogEntry.shredded`` attribute to ``True`` to show that this is no longer original log data.
|
||||
"""
|
||||
@@ -154,7 +151,6 @@ class BaseDataShredder:
|
||||
|
||||
def shred_log_fields(logentry, banlist=None, whitelist=None):
|
||||
d = logentry.parsed_data
|
||||
initial_data = copy.copy(d)
|
||||
shredded = False
|
||||
if whitelist:
|
||||
for k, v in d.items():
|
||||
@@ -166,61 +162,9 @@ def shred_log_fields(logentry, banlist=None, whitelist=None):
|
||||
if f in d:
|
||||
d[f] = '█'
|
||||
shredded = True
|
||||
if d != initial_data:
|
||||
logentry.data = json.dumps(d)
|
||||
logentry.shredded = logentry.shredded or shredded
|
||||
logentry.save(update_fields=['data', 'shredded'])
|
||||
|
||||
|
||||
def slow_update(qs, batch_size=1000, sleep_time=.5, progress_callback=None, progress_offset=0, progress_total=None, **update):
|
||||
"""
|
||||
Doing UPDATE queries on hundreds of thousands of rows can cause outages due to high write load on the database.
|
||||
This provides a throttled way to update rows. The condition for this to work properly is that the queryset has a
|
||||
filter condition that no longer applies after the update!
|
||||
Otherwise, this will be an endless loop!
|
||||
"""
|
||||
total_updated = 0
|
||||
while True:
|
||||
updated = qs.order_by().filter(
|
||||
pk__in=qs.order_by().values_list('pk', flat=True)[:batch_size]
|
||||
).update(**update)
|
||||
total_updated += updated
|
||||
if not updated:
|
||||
break
|
||||
if total_updated >= 0.8 * batch_size:
|
||||
time.sleep(sleep_time)
|
||||
if progress_callback and progress_total:
|
||||
progress_callback((progress_offset + total_updated) / progress_total)
|
||||
|
||||
return total_updated
|
||||
|
||||
|
||||
def slow_delete(qs, batch_size=1000, sleep_time=.5, progress_callback=None, progress_offset=0, progress_total=None):
|
||||
"""
|
||||
Doing DELETE queries on hundreds of thousands of rows can cause outages due to high write load on the database.
|
||||
This provides a throttled way to update rows.
|
||||
"""
|
||||
total_deleted = 0
|
||||
while True:
|
||||
deleted = qs.order_by().filter(
|
||||
pk__in=qs.order_by().values_list('pk', flat=True)[:batch_size]
|
||||
).delete()[0]
|
||||
total_deleted += deleted
|
||||
if not deleted:
|
||||
break
|
||||
if total_deleted >= 0.8 * batch_size:
|
||||
time.sleep(sleep_time)
|
||||
return total_deleted
|
||||
|
||||
|
||||
def _progress_helper(queryset, progress_callback, offset, total):
|
||||
if not progress_callback:
|
||||
yield from queryset
|
||||
else:
|
||||
for i, o in enumerate(queryset):
|
||||
yield o
|
||||
if i % 10 == 0:
|
||||
progress_callback((i + offset) / total * 100)
|
||||
logentry.data = json.dumps(d)
|
||||
logentry.shredded = logentry.shredded or shredded
|
||||
logentry.save(update_fields=['data', 'shredded'])
|
||||
|
||||
|
||||
class PhoneNumberShredder(BaseDataShredder):
|
||||
@@ -233,26 +177,18 @@ class PhoneNumberShredder(BaseDataShredder):
|
||||
o.code: o.phone for o in self.event.orders.filter(phone__isnull=False)
|
||||
}, cls=CustomJSONEncoder, indent=4)
|
||||
|
||||
def shred_data(self, progress_callback=None):
|
||||
qs_orders = self.event.orders.all()
|
||||
qs_orders_cnt = qs_orders.count()
|
||||
qs_le = self.event.logentry_set.filter(action_type="pretix.event.order.phone.changed")
|
||||
qs_le_cnt = qs_le.count()
|
||||
total = qs_le_cnt + qs_orders_cnt
|
||||
|
||||
for o in _progress_helper(qs_orders, progress_callback, 0, total):
|
||||
changed = bool(o.phone)
|
||||
@transaction.atomic
|
||||
def shred_data(self):
|
||||
for o in self.event.orders.all():
|
||||
o.phone = None
|
||||
d = o.meta_info_data
|
||||
if d:
|
||||
if 'contact_form_data' in d and 'phone' in d['contact_form_data']:
|
||||
changed = True
|
||||
del d['contact_form_data']['phone']
|
||||
o.meta_info = json.dumps(d)
|
||||
if changed:
|
||||
o.save(update_fields=['meta_info', 'phone'])
|
||||
o.meta_info = json.dumps(d)
|
||||
o.save(update_fields=['meta_info', 'phone'])
|
||||
|
||||
for le in _progress_helper(qs_le, progress_callback, qs_orders_cnt, total):
|
||||
for le in self.event.logentry_set.filter(action_type="pretix.event.order.phone.changed"):
|
||||
shred_log_fields(le, banlist=['old_phone', 'new_phone'])
|
||||
|
||||
|
||||
@@ -271,66 +207,37 @@ class EmailAddressShredder(BaseDataShredder):
|
||||
for op in OrderPosition.all.filter(order__event=self.event, attendee_email__isnull=False)
|
||||
}, indent=4)
|
||||
|
||||
def shred_data(self, progress_callback=None):
|
||||
qs_op = OrderPosition.all.filter(order__event=self.event, attendee_email__isnull=False)
|
||||
qs_op_cnt = qs_op.count()
|
||||
@transaction.atomic
|
||||
def shred_data(self):
|
||||
OrderPosition.all.filter(order__event=self.event, attendee_email__isnull=False).update(attendee_email=None)
|
||||
|
||||
qs_orders = self.event.orders.all()
|
||||
qs_orders_cnt = qs_orders.count()
|
||||
|
||||
qs_le = self.event.logentry_set.filter(
|
||||
Q(action_type__contains="order.email") | Q(action_type__contains="position.email") |
|
||||
Q(action_type="pretix.event.order.contact.changed") |
|
||||
Q(action_type="pretix.event.order.modified")
|
||||
).exclude(data="")
|
||||
qs_le_cnt = qs_le.count()
|
||||
|
||||
total = qs_op_cnt + qs_orders_cnt + qs_le_cnt
|
||||
|
||||
slow_update(
|
||||
qs_op,
|
||||
attendee_email=None,
|
||||
progress_callback=progress_callback,
|
||||
progress_offset=0,
|
||||
progress_total=total,
|
||||
# Updates to order position table are slow, since PostgreSQL needs to update many indexes, so let's
|
||||
# take them really slowly to not overwhelm the database.
|
||||
batch_size=100,
|
||||
sleep_time=2,
|
||||
)
|
||||
|
||||
for o in _progress_helper(qs_orders, progress_callback, qs_op_cnt, total):
|
||||
changed = bool(o.email) or bool(o.customer)
|
||||
for o in self.event.orders.all():
|
||||
o.email = None
|
||||
o.customer = None
|
||||
d = o.meta_info_data
|
||||
if d:
|
||||
if 'contact_form_data' in d and 'email' in d['contact_form_data']:
|
||||
del d['contact_form_data']['email']
|
||||
changed = True
|
||||
o.meta_info = json.dumps(d)
|
||||
if 'contact_form_data' in d and 'email_repeat' in d['contact_form_data']:
|
||||
del d['contact_form_data']['email_repeat']
|
||||
changed = True
|
||||
if changed:
|
||||
if d:
|
||||
o.meta_info = json.dumps(d)
|
||||
o.save(update_fields=['meta_info', 'email', 'customer'])
|
||||
o.meta_info = json.dumps(d)
|
||||
o.save(update_fields=['meta_info', 'email', 'customer'])
|
||||
|
||||
for le in _progress_helper(qs_le, progress_callback, qs_op_cnt + qs_orders_cnt, total):
|
||||
if le.action_type == "pretix.event.order.modified":
|
||||
d = le.parsed_data
|
||||
if 'data' in d:
|
||||
for row in d['data']:
|
||||
if 'attendee_email' in row:
|
||||
row['attendee_email'] = '█'
|
||||
le.data = json.dumps(d)
|
||||
le.shredded = True
|
||||
le.save(update_fields=['data', 'shredded'])
|
||||
else:
|
||||
shred_log_fields(le, banlist=[
|
||||
'recipient', 'message', 'subject', 'full_mail', 'old_email', 'new_email'
|
||||
])
|
||||
for le in self.event.logentry_set.filter(
|
||||
Q(action_type__contains="order.email") | Q(action_type__contains="position.email"),
|
||||
):
|
||||
shred_log_fields(le, banlist=['recipient', 'message', 'subject', 'full_mail'])
|
||||
|
||||
for le in self.event.logentry_set.filter(action_type="pretix.event.order.contact.changed"):
|
||||
shred_log_fields(le, banlist=['old_email', 'new_email'])
|
||||
|
||||
for le in self.event.logentry_set.filter(action_type="pretix.event.order.modified").exclude(data=""):
|
||||
d = le.parsed_data
|
||||
if 'data' in d:
|
||||
for row in d['data']:
|
||||
if 'attendee_email' in row:
|
||||
row['attendee_email'] = '█'
|
||||
le.data = json.dumps(d)
|
||||
le.shredded = True
|
||||
le.save(update_fields=['data', 'shredded'])
|
||||
|
||||
|
||||
class WaitingListShredder(BaseDataShredder):
|
||||
@@ -344,35 +251,16 @@ class WaitingListShredder(BaseDataShredder):
|
||||
for wle in self.event.waitinglistentries.all()
|
||||
], indent=4)
|
||||
|
||||
def shred_data(self, progress_callback=None):
|
||||
qs_wle = self.event.waitinglistentries.exclude(email='█')
|
||||
qs_wle_cnt = qs_wle.count()
|
||||
@transaction.atomic
|
||||
def shred_data(self):
|
||||
self.event.waitinglistentries.update(name_cached=None, name_parts={'_shredded': True}, email='█', phone='█')
|
||||
|
||||
qs_voucher = self.event.waitinglistentries.select_related('voucher').filter(voucher__isnull=False)
|
||||
qs_voucher_cnt = qs_voucher.count()
|
||||
|
||||
qs_le = self.event.logentry_set.filter(action_type="pretix.voucher.added.waitinglist").exclude(data="")
|
||||
qs_le_cnt = qs_le.count()
|
||||
|
||||
total = qs_voucher_cnt + qs_wle_cnt + qs_le_cnt
|
||||
|
||||
slow_update(
|
||||
qs_wle,
|
||||
name_cached=None,
|
||||
name_parts={'_shredded': True},
|
||||
email='█',
|
||||
phone='█',
|
||||
progress_callback=progress_callback,
|
||||
progress_offset=0,
|
||||
progress_total=total,
|
||||
)
|
||||
|
||||
for wle in _progress_helper(qs_voucher, progress_callback, qs_wle_cnt, total):
|
||||
for wle in self.event.waitinglistentries.select_related('voucher').filter(voucher__isnull=False):
|
||||
if '@' in wle.voucher.comment:
|
||||
wle.voucher.comment = '█'
|
||||
wle.voucher.save(update_fields=['comment'])
|
||||
|
||||
for le in _progress_helper(qs_le, progress_callback, qs_wle_cnt + qs_voucher_cnt, total):
|
||||
for le in self.event.logentry_set.filter(action_type="pretix.voucher.added.waitinglist").exclude(data=""):
|
||||
d = le.parsed_data
|
||||
if 'name' in d:
|
||||
d['name'] = '█'
|
||||
@@ -410,41 +298,17 @@ class AttendeeInfoShredder(BaseDataShredder):
|
||||
)
|
||||
}, indent=4)
|
||||
|
||||
def shred_data(self, progress_callback=None):
|
||||
qs_op = OrderPosition.all.filter(
|
||||
@transaction.atomic
|
||||
def shred_data(self):
|
||||
OrderPosition.all.filter(
|
||||
order__event=self.event
|
||||
).filter(
|
||||
Q(attendee_name_cached__isnull=False) |
|
||||
Q(company__isnull=False) |
|
||||
Q(street__isnull=False) |
|
||||
Q(zipcode__isnull=False) |
|
||||
Q(city__isnull=False)
|
||||
)
|
||||
qs_op_cnt = qs_op.count()
|
||||
Q(attendee_name_cached__isnull=False) | Q(attendee_name_parts__isnull=False) |
|
||||
Q(company__isnull=False) | Q(street__isnull=False) | Q(zipcode__isnull=False) | Q(city__isnull=False)
|
||||
).update(attendee_name_cached=None, attendee_name_parts={'_shredded': True}, company=None, street=None,
|
||||
zipcode=None, city=None)
|
||||
|
||||
qs_le = self.event.logentry_set.filter(action_type="pretix.event.order.modified").exclude(data="")
|
||||
qs_le_cnt = qs_le.count()
|
||||
|
||||
total = qs_op_cnt + qs_le_cnt
|
||||
|
||||
slow_update(
|
||||
qs_op,
|
||||
attendee_name_cached=None,
|
||||
attendee_name_parts={'_shredded': True},
|
||||
company=None,
|
||||
street=None,
|
||||
zipcode=None,
|
||||
city=None,
|
||||
progress_callback=progress_callback,
|
||||
progress_total=total,
|
||||
progress_offset=0,
|
||||
# Updates to order position table are slow, since PostgreSQL needs to update many indexes, so let's
|
||||
# take them really slowly to not overwhelm the database.
|
||||
batch_size=100,
|
||||
sleep_time=2,
|
||||
)
|
||||
|
||||
for le in _progress_helper(qs_le, progress_callback, qs_op_cnt, total):
|
||||
for le in self.event.logentry_set.filter(action_type="pretix.event.order.modified").exclude(data=""):
|
||||
d = le.parsed_data
|
||||
if 'data' in d:
|
||||
for i, row in enumerate(d['data']):
|
||||
@@ -479,18 +343,11 @@ class InvoiceAddressShredder(BaseDataShredder):
|
||||
for ia in InvoiceAddress.objects.filter(order__event=self.event)
|
||||
}, indent=4)
|
||||
|
||||
def shred_data(self, progress_callback=None):
|
||||
qs_ia = InvoiceAddress.objects.filter(order__event=self.event)
|
||||
qs_ia_cnt = qs_ia.count()
|
||||
@transaction.atomic
|
||||
def shred_data(self):
|
||||
InvoiceAddress.objects.filter(order__event=self.event).delete()
|
||||
|
||||
qs_le = self.event.logentry_set.filter(action_type="pretix.event.order.modified").exclude(data="")
|
||||
qs_le_cnt = qs_le.count()
|
||||
|
||||
total = qs_ia_cnt + qs_le_cnt
|
||||
|
||||
slow_delete(qs_ia, progress_callback=progress_callback, progress_total=total, progress_offset=0)
|
||||
|
||||
for le in _progress_helper(qs_le, progress_callback, qs_ia_cnt, total):
|
||||
for le in self.event.logentry_set.filter(action_type="pretix.event.order.modified").exclude(data=""):
|
||||
d = le.parsed_data
|
||||
if 'invoice_data' in d and not isinstance(d['invoice_data'], bool):
|
||||
for field in d['invoice_data']:
|
||||
@@ -518,18 +375,11 @@ class QuestionAnswerShredder(BaseDataShredder):
|
||||
).data
|
||||
yield 'question-answers.json', 'application/json', json.dumps(d, indent=4)
|
||||
|
||||
def shred_data(self, progress_callback=None):
|
||||
qs_qa = QuestionAnswer.objects.filter(orderposition__order__event=self.event)
|
||||
qs_qa_cnt = qs_qa.count()
|
||||
@transaction.atomic
|
||||
def shred_data(self):
|
||||
QuestionAnswer.objects.filter(orderposition__order__event=self.event).delete()
|
||||
|
||||
qs_le = self.event.logentry_set.filter(action_type="pretix.event.order.modified").exclude(data="")
|
||||
qs_le_cnt = qs_le.count()
|
||||
|
||||
total = qs_qa_cnt + qs_le_cnt
|
||||
|
||||
slow_delete(qs_qa, progress_callback=progress_callback, progress_total=total, progress_offset=0)
|
||||
|
||||
for le in _progress_helper(qs_le, progress_callback, qs_qa_cnt, total):
|
||||
for le in self.event.logentry_set.filter(action_type="pretix.event.order.modified").exclude(data=""):
|
||||
d = le.parsed_data
|
||||
if 'data' in d:
|
||||
for i, row in enumerate(d['data']):
|
||||
@@ -558,11 +408,9 @@ class InvoiceShredder(BaseDataShredder):
|
||||
yield 'invoices/{}.pdf'.format(i.number), 'application/pdf', i.file.read()
|
||||
i.file.close()
|
||||
|
||||
def shred_data(self, progress_callback=None):
|
||||
qs_i = self.event.invoices.filter(shredded=False)
|
||||
total = qs_i.count()
|
||||
|
||||
for i in _progress_helper(qs_i, progress_callback, 0, total):
|
||||
@transaction.atomic
|
||||
def shred_data(self):
|
||||
for i in self.event.invoices.filter(shredded=False):
|
||||
if i.file:
|
||||
i.file.delete()
|
||||
i.shredded = True
|
||||
@@ -582,17 +430,10 @@ class CachedTicketShredder(BaseDataShredder):
|
||||
def generate_files(self) -> List[Tuple[str, str, str]]:
|
||||
pass
|
||||
|
||||
def shred_data(self, progress_callback=None):
|
||||
qs_1 = CachedTicket.objects.filter(order_position__order__event=self.event)
|
||||
qs_1_cnt = qs_1.count()
|
||||
|
||||
qs_2 = CachedCombinedTicket.objects.filter(order__event=self.event)
|
||||
qs_2_cnt = qs_2.count()
|
||||
|
||||
total = qs_1_cnt + qs_2_cnt
|
||||
|
||||
slow_delete(qs_1, progress_callback=progress_callback, progress_total=total, progress_offset=0)
|
||||
slow_delete(qs_2, progress_callback=progress_callback, progress_total=total, progress_offset=qs_1_cnt)
|
||||
@transaction.atomic
|
||||
def shred_data(self):
|
||||
CachedTicket.objects.filter(order_position__order__event=self.event).delete()
|
||||
CachedCombinedTicket.objects.filter(order__event=self.event).delete()
|
||||
|
||||
|
||||
class PaymentInfoShredder(BaseDataShredder):
|
||||
@@ -605,21 +446,14 @@ class PaymentInfoShredder(BaseDataShredder):
|
||||
def generate_files(self) -> List[Tuple[str, str, str]]:
|
||||
pass
|
||||
|
||||
def shred_data(self, progress_callback=None):
|
||||
qs_p = OrderPayment.objects.filter(order__event=self.event)
|
||||
qs_p_count = qs_p.count()
|
||||
qs_r = OrderRefund.objects.filter(order__event=self.event)
|
||||
qs_r_count = qs_r.count()
|
||||
|
||||
total = qs_p_count + qs_r_count
|
||||
|
||||
@transaction.atomic
|
||||
def shred_data(self):
|
||||
provs = self.event.get_payment_providers()
|
||||
for obj in _progress_helper(qs_p, progress_callback, 0, total):
|
||||
for obj in OrderPayment.objects.filter(order__event=self.event):
|
||||
pprov = provs.get(obj.provider)
|
||||
if pprov:
|
||||
pprov.shred_payment_info(obj)
|
||||
|
||||
for obj in _progress_helper(qs_r, progress_callback, qs_p_count, total):
|
||||
for obj in OrderRefund.objects.filter(order__event=self.event):
|
||||
pprov = provs.get(obj.provider)
|
||||
if pprov:
|
||||
pprov.shred_payment_info(obj)
|
||||
|
||||
@@ -787,23 +787,3 @@ return a dictionary mapping names of attributes in the settings store to DRF ser
|
||||
|
||||
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
|
||||
"""
|
||||
|
||||
customer_created = GlobalSignal()
|
||||
"""
|
||||
Arguments: ``customer``
|
||||
|
||||
This signal is sent out every time a customer account is created. The ``customer``
|
||||
object is given as the first argument.
|
||||
|
||||
The ``sender`` keyword argument will contain the organizer.
|
||||
"""
|
||||
|
||||
customer_signed_in = GlobalSignal()
|
||||
"""
|
||||
Arguments: ``customer``
|
||||
|
||||
This signal is sent out every time a customer signs in. The ``customer`` object
|
||||
is given as the first argument.
|
||||
|
||||
The ``sender`` keyword argument will contain the organizer.
|
||||
"""
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
{% load i18n %}
|
||||
{% load i18n %}{% blocktrans with url=url|safe %}Hello,
|
||||
|
||||
we hereby confirm that the following data shredding job has been completed:
|
||||
|
||||
Organizer: {{ organizer }}
|
||||
|
||||
Event: {{ event }}
|
||||
|
||||
Data selection: {{ shredders }}
|
||||
|
||||
Start time: {{ start_time }} (new data added after this time might not have been deleted)
|
||||
|
||||
Best regards,
|
||||
|
||||
Your pretix team
|
||||
{% endblocktrans %}
|
||||
@@ -48,8 +48,6 @@ from django.utils.http import url_has_allowed_host_and_scheme
|
||||
from django.utils.safestring import mark_safe
|
||||
from markdown import Extension
|
||||
from markdown.inlinepatterns import SubstituteTagInlineProcessor
|
||||
from markdown.postprocessors import Postprocessor
|
||||
from markdown.treeprocessors import UnescapeTreeprocessor
|
||||
from tlds import tld_set
|
||||
|
||||
register = template.Library()
|
||||
@@ -110,8 +108,6 @@ URL_RE = SimpleLazyObject(lambda: build_url_re(tlds=sorted(tld_set, key=len, rev
|
||||
|
||||
EMAIL_RE = SimpleLazyObject(lambda: build_email_re(tlds=sorted(tld_set, key=len, reverse=True)))
|
||||
|
||||
DOT_ESCAPE = "|escaped-dot-sGnY9LMK|"
|
||||
|
||||
|
||||
def safelink_callback(attrs, new=False):
|
||||
"""
|
||||
@@ -189,109 +185,6 @@ class EmailNl2BrExtension(Extension):
|
||||
md.inlinePatterns.register(br_tag, 'nl', 5)
|
||||
|
||||
|
||||
class LinkifyPostprocessor(Postprocessor):
|
||||
def __init__(self, linker):
|
||||
self.linker = linker
|
||||
super().__init__()
|
||||
|
||||
def run(self, text):
|
||||
return self.linker.linkify(text)
|
||||
|
||||
|
||||
class CleanPostprocessor(Postprocessor):
|
||||
def __init__(self, tags, attributes, protocols, strip):
|
||||
self.tags = tags
|
||||
self.attributes = attributes
|
||||
self.protocols = protocols
|
||||
self.strip = strip
|
||||
super().__init__()
|
||||
|
||||
def run(self, text):
|
||||
return bleach.clean(
|
||||
text,
|
||||
tags=self.tags,
|
||||
attributes=self.attributes,
|
||||
protocols=self.protocols,
|
||||
strip=self.strip
|
||||
)
|
||||
|
||||
|
||||
class CustomUnescapeTreeprocessor(UnescapeTreeprocessor):
|
||||
"""
|
||||
This un-escapes everything except \\.
|
||||
"""
|
||||
|
||||
def _unescape(self, m):
|
||||
if m.group(1) == "46": # 46 is the ASCII position of .
|
||||
return DOT_ESCAPE
|
||||
return chr(int(m.group(1)))
|
||||
|
||||
|
||||
class CustomUnescapePostprocessor(Postprocessor):
|
||||
"""
|
||||
Restore escaped .
|
||||
"""
|
||||
|
||||
def run(self, text):
|
||||
return text.replace(DOT_ESCAPE, ".")
|
||||
|
||||
|
||||
class LinkifyAndCleanExtension(Extension):
|
||||
r"""
|
||||
We want to do:
|
||||
|
||||
input --> markdown --> bleach clean --> linkify --> output
|
||||
|
||||
Internally, the markdown library does:
|
||||
|
||||
source --> parse --> (tree|inline)processors --> serializing --> postprocessors
|
||||
|
||||
All escaped characters such as \. will be turned to something like <STX>46<ETX> in the processors
|
||||
step and then will be converted to . back again in the last tree processor, before serialization.
|
||||
Therefore, linkify does not see the escaped character anymore. This is annoying for the one case
|
||||
where you want to type "rich_text.py" and *not* have it turned into a link, since you can't type
|
||||
"rich_text\.py" either.
|
||||
|
||||
A simple solution would be to run linkify before markdown, but that may cause other issues when
|
||||
linkify messes with the markdown syntax and it makes handling our attributes etc. harder.
|
||||
|
||||
So we do a weird hack where we modify the unescape processor to unescape everything EXCEPT for the
|
||||
dot and then unescape that one manually after linkify. However, to make things even harder, the bleach
|
||||
clean step removes any invisible characters, so we need to cheat a bit more.
|
||||
"""
|
||||
|
||||
def __init__(self, linker, tags, attributes, protocols, strip):
|
||||
self.linker = linker
|
||||
self.tags = tags
|
||||
self.attributes = attributes
|
||||
self.protocols = protocols
|
||||
self.strip = strip
|
||||
super().__init__()
|
||||
|
||||
def extendMarkdown(self, md):
|
||||
md.treeprocessors.deregister('unescape')
|
||||
md.treeprocessors.register(
|
||||
CustomUnescapeTreeprocessor(md),
|
||||
'unescape',
|
||||
0
|
||||
)
|
||||
md.postprocessors.register(
|
||||
CleanPostprocessor(self.tags, self.attributes, self.protocols, self.strip),
|
||||
'clean',
|
||||
2
|
||||
)
|
||||
md.postprocessors.register(
|
||||
LinkifyPostprocessor(self.linker),
|
||||
'linkify',
|
||||
1
|
||||
)
|
||||
md.postprocessors.register(
|
||||
CustomUnescapePostprocessor(self.linker),
|
||||
'unescape_dot',
|
||||
0
|
||||
)
|
||||
|
||||
|
||||
def markdown_compile_email(source):
|
||||
linker = bleach.Linker(
|
||||
url_re=URL_RE,
|
||||
@@ -299,20 +192,18 @@ def markdown_compile_email(source):
|
||||
callbacks=DEFAULT_CALLBACKS + [truelink_callback, abslink_callback],
|
||||
parse_email=True
|
||||
)
|
||||
return markdown.markdown(
|
||||
source,
|
||||
extensions=[
|
||||
'markdown.extensions.sane_lists',
|
||||
EmailNl2BrExtension(),
|
||||
LinkifyAndCleanExtension(
|
||||
linker,
|
||||
tags=ALLOWED_TAGS,
|
||||
attributes=ALLOWED_ATTRIBUTES,
|
||||
protocols=ALLOWED_PROTOCOLS,
|
||||
strip=False,
|
||||
)
|
||||
]
|
||||
)
|
||||
return linker.linkify(bleach.clean(
|
||||
markdown.markdown(
|
||||
source,
|
||||
extensions=[
|
||||
'markdown.extensions.sane_lists',
|
||||
EmailNl2BrExtension(),
|
||||
]
|
||||
),
|
||||
tags=ALLOWED_TAGS,
|
||||
attributes=ALLOWED_ATTRIBUTES,
|
||||
protocols=ALLOWED_PROTOCOLS,
|
||||
))
|
||||
|
||||
|
||||
class SnippetExtension(markdown.extensions.Extension):
|
||||
@@ -322,24 +213,23 @@ class SnippetExtension(markdown.extensions.Extension):
|
||||
md.parser.blockprocessors.deregister('quote')
|
||||
|
||||
|
||||
def markdown_compile(source, linker, snippet=False):
|
||||
def markdown_compile(source, snippet=False):
|
||||
tags = ALLOWED_TAGS_SNIPPET if snippet else ALLOWED_TAGS
|
||||
exts = [
|
||||
'markdown.extensions.sane_lists',
|
||||
'markdown.extensions.nl2br',
|
||||
LinkifyAndCleanExtension(
|
||||
linker,
|
||||
tags=tags,
|
||||
attributes=ALLOWED_ATTRIBUTES,
|
||||
protocols=ALLOWED_PROTOCOLS,
|
||||
strip=snippet,
|
||||
)
|
||||
'markdown.extensions.nl2br'
|
||||
]
|
||||
if snippet:
|
||||
exts.append(SnippetExtension())
|
||||
return markdown.markdown(
|
||||
source,
|
||||
extensions=exts
|
||||
return bleach.clean(
|
||||
markdown.markdown(
|
||||
source,
|
||||
extensions=exts
|
||||
),
|
||||
strip=snippet,
|
||||
tags=tags,
|
||||
attributes=ALLOWED_ATTRIBUTES,
|
||||
protocols=ALLOWED_PROTOCOLS,
|
||||
)
|
||||
|
||||
|
||||
@@ -355,7 +245,7 @@ def rich_text(text: str, **kwargs):
|
||||
callbacks=DEFAULT_CALLBACKS + ([truelink_callback, safelink_callback] if kwargs.get('safelinks', True) else [truelink_callback, abslink_callback]),
|
||||
parse_email=True
|
||||
)
|
||||
body_md = markdown_compile(text, linker)
|
||||
body_md = linker.linkify(markdown_compile(text))
|
||||
return mark_safe(body_md)
|
||||
|
||||
|
||||
@@ -371,5 +261,5 @@ def rich_text_snippet(text: str, **kwargs):
|
||||
callbacks=DEFAULT_CALLBACKS + ([truelink_callback, safelink_callback] if kwargs.get('safelinks', True) else [truelink_callback, abslink_callback]),
|
||||
parse_email=True
|
||||
)
|
||||
body_md = markdown_compile(text, linker, snippet=True)
|
||||
body_md = linker.linkify(markdown_compile(text, snippet=True))
|
||||
return mark_safe(body_md)
|
||||
|
||||
@@ -115,8 +115,7 @@ class AsyncMixin:
|
||||
elif state == 'PROGRESS':
|
||||
data.update({
|
||||
'started': True,
|
||||
'percentage': info.get('value', 0) if isinstance(info, dict) else 0,
|
||||
'steps': info.get('steps', []) if isinstance(info, dict) else None,
|
||||
'percentage': info.get('value', 0) if isinstance(info, dict) else 0
|
||||
})
|
||||
elif state == 'STARTED':
|
||||
data.update({
|
||||
|
||||
@@ -127,7 +127,7 @@ class ClearableBasenameFileInput(forms.ClearableFileInput):
|
||||
|
||||
@property
|
||||
def is_img(self):
|
||||
return any(self.file.name.lower().endswith(e) for e in settings.FILE_UPLOAD_EXTENSIONS_IMAGE)
|
||||
return any(self.file.name.lower().endswith(e) for e in ('.jpg', '.jpeg', '.png', '.gif'))
|
||||
|
||||
def __str__(self):
|
||||
if hasattr(self.file, 'display_name'):
|
||||
|
||||
@@ -749,7 +749,6 @@ class PaymentSettingsForm(EventSettingsValidationMixin, SettingsForm):
|
||||
'payment_term_minutes',
|
||||
'payment_term_last',
|
||||
'payment_term_expire_automatically',
|
||||
'payment_term_expire_delay_days',
|
||||
'payment_term_accept_late',
|
||||
'payment_pending_hidden',
|
||||
'payment_explanation',
|
||||
@@ -1116,16 +1115,6 @@ class MailSettingsForm(SettingsForm):
|
||||
help_text=_("This email only applies to payment methods that can receive incomplete payments, "
|
||||
"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(
|
||||
label=_("Subject"),
|
||||
required=False,
|
||||
@@ -1299,8 +1288,6 @@ class MailSettingsForm(SettingsForm):
|
||||
'mail_subject_order_pending_warning': ['event', 'order'],
|
||||
'mail_text_order_incomplete_payment': ['event', 'order', 'pending_sum'],
|
||||
'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_download_reminder': ['event', 'order'],
|
||||
'mail_subject_download_reminder': ['event', 'order'],
|
||||
|
||||
@@ -412,15 +412,11 @@ class OrganizerSettingsForm(SettingsForm):
|
||||
'reusable_media_type_nfc_uid',
|
||||
'reusable_media_type_nfc_uid_autocreate_giftcard',
|
||||
'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(
|
||||
label=_('Header image'),
|
||||
ext_whitelist=settings.FILE_UPLOAD_EXTENSIONS_IMAGE,
|
||||
ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"),
|
||||
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
|
||||
required=False,
|
||||
help_text=_('If you provide a logo image, we will by default not show your organization name '
|
||||
@@ -430,7 +426,7 @@ class OrganizerSettingsForm(SettingsForm):
|
||||
)
|
||||
favicon = ExtFileField(
|
||||
label=_('Favicon'),
|
||||
ext_whitelist=settings.FILE_UPLOAD_EXTENSIONS_FAVICON,
|
||||
ext_whitelist=(".ico", ".png", ".jpg", ".gif", ".jpeg"),
|
||||
required=False,
|
||||
max_size=settings.FILE_UPLOAD_MAX_SIZE_FAVICON,
|
||||
help_text=_('If you provide a favicon, we will show it instead of the default pretix icon. '
|
||||
|
||||
@@ -368,13 +368,10 @@ def pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs):
|
||||
'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.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.event.comment': _('The event\'s internal comment has been updated.'),
|
||||
'pretix.event.canceled': _('The event has been canceled.'),
|
||||
'pretix.event.deleted': _('An event has been deleted.'),
|
||||
'pretix.event.shredder.started': _('A removal process for personal data has been started.'),
|
||||
'pretix.event.shredder.completed': _('A removal process for personal data has been completed.'),
|
||||
'pretix.event.order.modified': _('The order details have been changed.'),
|
||||
'pretix.event.order.unpaid': _('The order has been marked as unpaid.'),
|
||||
'pretix.event.order.secret.changed': _('The order\'s secret has been changed.'),
|
||||
@@ -433,7 +430,6 @@ def pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs):
|
||||
'the order has been received and requires '
|
||||
'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.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.canceled': _('Payment {local_id} has been canceled.'),
|
||||
'pretix.event.order.payment.canceled.failed': _('Canceling payment {local_id} has failed.'),
|
||||
|
||||
@@ -304,10 +304,6 @@ 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
|
||||
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.
|
||||
"""
|
||||
|
||||
|
||||
@@ -470,8 +470,6 @@
|
||||
<div class="progress-bar progress-bar-success">
|
||||
</div>
|
||||
</div>
|
||||
<div class="steps">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
</div>
|
||||
<div class="panel-body form-horizontal">
|
||||
{% if spf_warning %}
|
||||
<div class="alert alert-danger">
|
||||
<div class="alert alert-warning">
|
||||
<p>
|
||||
{{ spf_warning }}
|
||||
</p>
|
||||
@@ -70,18 +70,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if spf_warning %}
|
||||
<div class="form-group submit-group">
|
||||
<a href="" class="btn btn-default btn-save">
|
||||
{% trans "Cancel" %}
|
||||
</a>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="form-group submit-group">
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
{% trans "Save" %}
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="form-group submit-group">
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
{% trans "Save" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
@@ -105,9 +105,6 @@
|
||||
{% 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" %}
|
||||
|
||||
{% 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 %}
|
||||
{% 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" %}
|
||||
|
||||
|
||||
@@ -65,8 +65,6 @@
|
||||
{% bootstrap_field form.payment_term_minutes layout="control" %}
|
||||
{% bootstrap_field form.payment_term_last layout="control" %}
|
||||
{% bootstrap_field form.payment_term_expire_automatically layout="control" %}
|
||||
{% trans "days" context "unit" as days %}
|
||||
{% bootstrap_field form.payment_term_expire_delay_days layout="control" addon_after=days %}
|
||||
{% bootstrap_field form.payment_term_accept_late layout="control" %}
|
||||
{% bootstrap_field form.payment_pending_hidden layout="control" %}
|
||||
</fieldset>
|
||||
|
||||
@@ -186,12 +186,8 @@
|
||||
{% bootstrap_field form.media_type layout="control" %}
|
||||
{% endif %}
|
||||
{% for f in plugin_forms %}
|
||||
{% if f.is_layouts and not f.title %}
|
||||
{% if f.template and not "template" in f.fields %}
|
||||
{% include f.template with form=f %}
|
||||
{% else %}
|
||||
{% bootstrap_form f layout="control" %}
|
||||
{% endif %}
|
||||
{% if f.is_layouts %}
|
||||
{% bootstrap_form f layout="control" %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</fieldset>
|
||||
@@ -260,27 +256,11 @@
|
||||
{% endif %}
|
||||
{% bootstrap_field form.show_quota_left layout="control" %}
|
||||
{% for f in plugin_forms %}
|
||||
{% if not f.is_layouts and not f.title %}
|
||||
{% if f.template and not "template" in f.fields %}
|
||||
{% include f.template with form=f %}
|
||||
{% else %}
|
||||
{% bootstrap_form f layout="control" %}
|
||||
{% endif %}
|
||||
{% if not f.is_layouts %}
|
||||
{% bootstrap_form f layout="control" %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</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 class="form-group submit-group">
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
|
||||
@@ -262,32 +262,6 @@
|
||||
</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>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
{% trans "Data shredder" %}
|
||||
</h1>
|
||||
<form action="{% url "control:event.shredder.shred" event=request.event.slug organizer=request.organizer.slug %}"
|
||||
method="post" class="form-horizontal" data-asynctask data-asynctask-long>
|
||||
method="post" class="form-horizontal" data-asynctask>
|
||||
{% csrf_token %}
|
||||
<fieldset>
|
||||
{% if download_on_shred %}
|
||||
@@ -55,12 +55,6 @@
|
||||
</fieldset>
|
||||
{% endif %}
|
||||
<input type="hidden" name="file" value="{{ file.pk }}">
|
||||
<div class="alert alert-info">
|
||||
{% blocktrans trimmed %}
|
||||
Depending on the amount of data in your event, the following step may take a while to complete.
|
||||
We will inform you via email once it has been completed.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
<div class="form-group submit-group">
|
||||
<button type="submit" class="btn btn-primary btn-save">
|
||||
{% trans "Continue" %}
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
</div>
|
||||
{% else %}
|
||||
<form action="{% url "control:event.shredder.export" event=request.event.slug organizer=request.organizer.slug %}"
|
||||
method="post" class="form-horizontal" data-asynctask data-asynctask-long>
|
||||
method="post" class="form-horizontal" data-asynctask>
|
||||
<legend>{% trans "Data selection" %}</legend>
|
||||
{% csrf_token %}
|
||||
<div class="panel-group" id="payment_accordion">
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
placeholder="{% trans "Prefix (optional)" %}">
|
||||
<div class="input-group">
|
||||
<input type="number" class="form-control input-xs"
|
||||
id="voucher-bulk-codes-num" max="100000"
|
||||
id="voucher-bulk-codes-num"
|
||||
placeholder="{% trans "Number" context "number_of_things" %}">
|
||||
<div class="input-group-btn">
|
||||
<button class="btn btn-default" type="button" id="voucher-bulk-codes-generate"
|
||||
|
||||
@@ -212,21 +212,11 @@
|
||||
<span class="label label-warning">{% trans "Voucher assigned" %}</span>
|
||||
{% endif %}
|
||||
{% elif e.availability.0 == 100 %}
|
||||
{% if e.availability.1|default_if_none:"none" == "none" %}
|
||||
<span class="label label-danger" data-toggle="tooltip"
|
||||
title="{% trans "For safety reasons, the waiting list does not run if the quota is set to unlimited." %}">
|
||||
<span class="fa fa-ban" aria-hidden="true"></span>
|
||||
{% blocktrans trimmed %}
|
||||
Quota unlimited
|
||||
{% endblocktrans %}
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="label label-warning">
|
||||
{% blocktrans with num=e.availability.1 %}
|
||||
Waiting, product {{ num }}x available
|
||||
{% endblocktrans %}
|
||||
</span>
|
||||
{% endif %}
|
||||
<span class="label label-warning">
|
||||
{% blocktrans with num=e.availability.1 %}
|
||||
Waiting, product {{ num }}x available
|
||||
{% endblocktrans %}
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="label label-danger">{% trans "Waiting, product unavailable" %}</span>
|
||||
{% endif %}
|
||||
@@ -236,7 +226,7 @@
|
||||
<a href="{% url "control:event.voucher" organizer=request.event.organizer.slug event=request.event.slug voucher=e.voucher.pk %}">
|
||||
{{ e.voucher }}
|
||||
</a>
|
||||
{% elif not e.voucher and e.availability.0 == 100 and e.availability.1|default_if_none:"none" != "none" %}
|
||||
{% elif not e.voucher and e.availability.0 == 100 %}
|
||||
<button name="assign" value="{{ e.pk }}" class="btn btn-default btn-xs">
|
||||
{% trans "Send a voucher" %}
|
||||
</button>
|
||||
|
||||
@@ -530,8 +530,7 @@ class CheckInListSimulator(EventPermissionRequiredMixin, FormView):
|
||||
and (self.result["status"] in ("ok", "incomplete") or self.result["reason"] == "rules"):
|
||||
op = OrderPosition.objects.get(pk=self.result["position"]["id"])
|
||||
rule_data = LazyRuleVars(op, self.list, form.cleaned_data["datetime"])
|
||||
rule_graph = _logic_annotate_for_graphic_explain(self.list.rules, op.subevent or self.list.event, rule_data,
|
||||
form.cleaned_data["datetime"])
|
||||
rule_graph = _logic_annotate_for_graphic_explain(self.list.rules, op.subevent or self.list.event, rule_data)
|
||||
self.result["rule_graph"] = rule_graph
|
||||
|
||||
if self.result.get("questions"):
|
||||
|
||||
@@ -783,8 +783,8 @@ class MailSettingsRendererPreview(MailSettingsPreview):
|
||||
return ctx
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
v = str(request.event.settings.mail_text_order_payment_failed)
|
||||
v = format_map(v, self.placeholders('mail_text_order_payment_failed'))
|
||||
v = str(request.event.settings.mail_text_order_placed)
|
||||
v = format_map(v, self.placeholders('mail_text_order_placed'))
|
||||
renderers = request.event.get_html_mail_renderers()
|
||||
if request.GET.get('renderer') in renderers:
|
||||
with rolledback_transaction():
|
||||
|
||||
@@ -88,10 +88,6 @@ from ...helpers.compat import CompatDeleteView
|
||||
from . import ChartContainingView, CreateView, PaginationMixin, UpdateView
|
||||
|
||||
|
||||
def has_truthy_attr(cls, attr):
|
||||
return hasattr(cls, attr) and getattr(cls, attr)
|
||||
|
||||
|
||||
class ItemList(ListView):
|
||||
model = Item
|
||||
context_object_name = 'items'
|
||||
@@ -1297,11 +1293,6 @@ class ItemUpdateGeneral(ItemDetailMixin, EventPermissionRequiredMixin, MetaDataE
|
||||
forms.extend(resp)
|
||||
else:
|
||||
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
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
|
||||
@@ -192,8 +192,8 @@ class MailSettingsSetupView(TemplateView):
|
||||
spf_record = get_spf_record(hostname)
|
||||
if not spf_record:
|
||||
spf_warning = _(
|
||||
'We could not find an SPF record set for the domain you are trying to use. This means that '
|
||||
'there is a very high change most of the emails will be rejected or marked as spam. We '
|
||||
'We could not find an SPF record set for the domain you are trying to use. You can still '
|
||||
'proceed, but it will increase the chance of emails going to spam or being rejected. We '
|
||||
'strongly recommend setting an SPF record on the domain. You can do so through the DNS '
|
||||
'settings at the provider you registered your domain with.'
|
||||
)
|
||||
@@ -205,8 +205,7 @@ class MailSettingsSetupView(TemplateView):
|
||||
'this system in the SPF record.'
|
||||
)
|
||||
|
||||
verification = settings.MAIL_CUSTOM_SENDER_VERIFICATION_REQUIRED and not spf_warning
|
||||
if verification:
|
||||
if settings.MAIL_CUSTOM_SENDER_VERIFICATION_REQUIRED:
|
||||
if 'verification' in self.request.POST:
|
||||
messages.error(request, _('The verification code was incorrect, please try again.'))
|
||||
else:
|
||||
@@ -231,7 +230,7 @@ class MailSettingsSetupView(TemplateView):
|
||||
context={
|
||||
'basetpl': self.basetpl,
|
||||
'object': self.object,
|
||||
'verification': verification,
|
||||
'verification': settings.MAIL_CUSTOM_SENDER_VERIFICATION_REQUIRED,
|
||||
'spf_warning': spf_warning,
|
||||
'spf_record': spf_record,
|
||||
'spf_key': settings.MAIL_CUSTOM_SENDER_SPF_STRING,
|
||||
|
||||
@@ -1918,7 +1918,7 @@ class OrderContactChange(OrderView):
|
||||
'pretix.event.order.contact.changed',
|
||||
data={
|
||||
'old_email': old_email,
|
||||
'new_email': self.form.cleaned_data.get('email'),
|
||||
'new_email': self.form.cleaned_data['email'],
|
||||
},
|
||||
user=self.request.user,
|
||||
)
|
||||
@@ -1930,7 +1930,7 @@ class OrderContactChange(OrderView):
|
||||
'pretix.event.order.phone.changed',
|
||||
data={
|
||||
'old_phone': old_phone,
|
||||
'new_phone': self.form.cleaned_data.get('phone'),
|
||||
'new_phone': self.form.cleaned_data['phone'],
|
||||
},
|
||||
user=self.request.user,
|
||||
)
|
||||
|
||||
@@ -396,7 +396,6 @@ class OrganizerDelete(AdministratorPermissionRequiredMixin, FormView):
|
||||
)
|
||||
self.request.organizer.delete_sub_objects()
|
||||
self.request.organizer.delete()
|
||||
self.request.organizer = None
|
||||
messages.success(self.request, _('The organizer has been deleted.'))
|
||||
return redirect(self.get_success_url())
|
||||
except ProtectedError as e:
|
||||
|
||||
@@ -37,11 +37,10 @@ import logging
|
||||
from collections import OrderedDict
|
||||
from zipfile import ZipFile
|
||||
|
||||
from django.contrib import messages
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.urls import reverse
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.translation import get_language, gettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views import View
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
@@ -63,16 +62,6 @@ class ShredderMixin:
|
||||
sorted(self.request.event.get_data_shredders().items(), key=lambda s: s[1].verbose_name)
|
||||
)
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
try:
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
except ShredError as e:
|
||||
messages.error(request, str(e))
|
||||
return redirect(reverse('control:event.shredder.start', kwargs={
|
||||
'event': self.request.event.slug,
|
||||
'organizer': self.request.event.organizer.slug
|
||||
}))
|
||||
|
||||
|
||||
class StartShredView(RecentAuthenticationRequiredMixin, EventPermissionRequiredMixin, ShredderMixin, TemplateView):
|
||||
permission = 'can_change_orders'
|
||||
@@ -178,5 +167,4 @@ class ShredDoView(RecentAuthenticationRequiredMixin, EventPermissionRequiredMixi
|
||||
if request.event.slug != request.POST.get("slug"):
|
||||
return self.error(ShredError(_("The slug you entered was not correct.")))
|
||||
|
||||
return self.do(self.request.event.id, request.POST.get("file"), request.POST.get("confirm_code"),
|
||||
self.request.user.pk, get_language())
|
||||
return self.do(self.request.event.id, request.POST.get("file"), request.POST.get("confirm_code"))
|
||||
|
||||
@@ -569,8 +569,6 @@ class VoucherRNG(EventPermissionRequiredMixin, View):
|
||||
def get(self, request, *args, **kwargs):
|
||||
try:
|
||||
num = int(request.GET.get('num', '5'))
|
||||
if num > 100_000:
|
||||
return HttpResponseBadRequest()
|
||||
except ValueError: # NOQA
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ class WaitingListQuerySetMixin:
|
||||
return self.request.POST
|
||||
return self.request.GET
|
||||
|
||||
def get_queryset(self, force_filtered=False):
|
||||
def get_queryset(self):
|
||||
qs = WaitingListEntry.objects.filter(
|
||||
event=self.request.event
|
||||
).select_related('item', 'variation', 'voucher').prefetch_related(
|
||||
@@ -135,8 +135,6 @@ class WaitingListQuerySetMixin:
|
||||
qs = qs.filter(
|
||||
id__in=self.request_data.getlist('entry')
|
||||
)
|
||||
elif force_filtered and '__ALL' not in self.request_data:
|
||||
qs = qs.none()
|
||||
|
||||
return qs
|
||||
|
||||
@@ -160,7 +158,7 @@ class WaitingListActionView(EventPermissionRequiredMixin, WaitingListQuerySetMix
|
||||
'forbidden': self.get_queryset().filter(voucher__isnull=False),
|
||||
})
|
||||
elif request.POST.get('action') == 'delete_confirm':
|
||||
for obj in self.get_queryset(force_filtered=True):
|
||||
for obj in self.get_queryset():
|
||||
if not obj.voucher_id:
|
||||
obj.log_action('pretix.event.orders.waitinglist.deleted', user=self.request.user)
|
||||
obj.delete()
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
import logging
|
||||
from io import BytesIO
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from PIL.Image import MAX_IMAGE_PIXELS, DecompressionBombError
|
||||
@@ -52,7 +51,7 @@ def validate_uploaded_file_for_valid_image(f):
|
||||
|
||||
try:
|
||||
try:
|
||||
image = Image.open(file, formats=settings.PILLOW_FORMATS_QUESTIONS_IMAGE)
|
||||
image = Image.open(file)
|
||||
# verify() must be called immediately after the constructor.
|
||||
image.verify()
|
||||
except DecompressionBombError:
|
||||
|
||||
@@ -21,8 +21,6 @@
|
||||
#
|
||||
from datetime import datetime
|
||||
|
||||
from PIL import Image
|
||||
|
||||
|
||||
def monkeypatch_vobject_performance():
|
||||
"""
|
||||
@@ -54,19 +52,5 @@ def monkeypatch_vobject_performance():
|
||||
icalendar.tzinfo_eq = new_tzinfo_eq
|
||||
|
||||
|
||||
def monkeypatch_pillow_safer():
|
||||
"""
|
||||
Pillow supports many file formats, among them EPS. For EPS, Pillow loads GhostScript whenever GhostScript
|
||||
is installed (cannot officially be disabled). However, GhostScript is known for regular security vulnerabilities.
|
||||
We have no use of reading EPS files and usually prevent this by using `Image.open(…, formats=[…])` to disable EPS
|
||||
support explicitly. However, we are worried about our dependencies like reportlab using `Image.open` without the
|
||||
`formats=` parameter. Therefore, as a defense in depth approach, we monkeypatch EPS support away by modifying the
|
||||
internal image format registry of Pillow.
|
||||
"""
|
||||
if "EPS" in Image.ID:
|
||||
Image.ID.remove("EPS")
|
||||
|
||||
|
||||
def monkeypatch_all_at_ready():
|
||||
monkeypatch_vobject_performance()
|
||||
monkeypatch_pillow_safer()
|
||||
|
||||
@@ -20,9 +20,8 @@
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
from arabic_reshaper import ArabicReshaper
|
||||
from django.conf import settings
|
||||
from django.utils.functional import SimpleLazyObject
|
||||
from PIL import Image
|
||||
from PIL.Image import Resampling
|
||||
from reportlab.lib.utils import ImageReader
|
||||
|
||||
|
||||
@@ -34,7 +33,7 @@ class ThumbnailingImageReader(ImageReader):
|
||||
height = width * self._image.size[1] / self._image.size[0]
|
||||
self._image.thumbnail(
|
||||
size=(int(width * dpi / 72), int(height * dpi / 72)),
|
||||
resample=Image.Resampling.BICUBIC
|
||||
resample=Resampling.BICUBIC
|
||||
)
|
||||
self._data = None
|
||||
return width, height
|
||||
@@ -45,9 +44,6 @@ class ThumbnailingImageReader(ImageReader):
|
||||
# (smaller) size of the modified image.
|
||||
return None
|
||||
|
||||
def _read_image(self, fp):
|
||||
return Image.open(fp, formats=settings.PILLOW_FORMATS_IMAGE)
|
||||
|
||||
|
||||
reshaper = SimpleLazyObject(lambda: ArabicReshaper(configuration={
|
||||
'delete_harakat': True,
|
||||
|
||||
@@ -23,7 +23,6 @@ import hashlib
|
||||
import math
|
||||
from io import BytesIO
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.files.base import ContentFile
|
||||
from django.core.files.storage import default_storage
|
||||
from PIL import Image, ImageOps, ImageSequence
|
||||
@@ -166,7 +165,7 @@ def resize_image(image, size):
|
||||
|
||||
def create_thumbnail(sourcename, size):
|
||||
source = default_storage.open(sourcename)
|
||||
image = Image.open(BytesIO(source.read()), formats=settings.PILLOW_FORMATS_QUESTIONS_IMAGE)
|
||||
image = Image.open(BytesIO(source.read()))
|
||||
try:
|
||||
image.load()
|
||||
except:
|
||||
|
||||
+1923
-2313
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-07-21 11:46+0000\n"
|
||||
"POT-Creation-Date: 2023-06-16 14:35+0000\n"
|
||||
"PO-Revision-Date: 2021-09-15 11:22+0000\n"
|
||||
"Last-Translator: Mohamed Tawfiq <mtawfiq@wafyapp.com>\n"
|
||||
"Language-Team: Arabic <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
@@ -39,7 +39,6 @@ msgid "Venmo"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
||||
#: pretix/static/pretixpresale/js/walletdetection.js:38
|
||||
msgid "Apple Pay"
|
||||
msgstr ""
|
||||
|
||||
@@ -139,8 +138,8 @@ msgid "Continue"
|
||||
msgstr "المتابعة"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:204
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:235
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:157
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:188
|
||||
msgid "Confirming your payment …"
|
||||
msgstr "جاري تأكيد الدفع الخاص بك …"
|
||||
|
||||
@@ -162,15 +161,15 @@ msgstr "الطلبات المدفوعة"
|
||||
msgid "Total revenue"
|
||||
msgstr "إجمالي الإيرادات"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:13
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:12
|
||||
msgid "Contacting Stripe …"
|
||||
msgstr "جاري الاتصال بStripe …"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:70
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:65
|
||||
msgid "Total"
|
||||
msgstr "المجموع"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:211
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:164
|
||||
msgid "Contacting your bank …"
|
||||
msgstr "جاري الاتصال بالبنك الذي تتعامل معه …"
|
||||
|
||||
@@ -331,8 +330,8 @@ msgstr "حاليا بالداخل"
|
||||
msgid "close"
|
||||
msgstr "إغلاق"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:58
|
||||
#: pretix/static/pretixbase/js/asynctask.js:135
|
||||
#: pretix/static/pretixbase/js/asynctask.js:43
|
||||
#: pretix/static/pretixbase/js/asynctask.js:120
|
||||
msgid ""
|
||||
"Your request is currently being processed. Depending on the size of your "
|
||||
"event, this might take up to a few minutes."
|
||||
@@ -340,13 +339,13 @@ msgstr ""
|
||||
"تتم معالجة طلبك حاليا. قد يستغرق الأمر بضع دقائق بناء على حجم الفعالية التي "
|
||||
"اخترت."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:63
|
||||
#: pretix/static/pretixbase/js/asynctask.js:140
|
||||
#: pretix/static/pretixbase/js/asynctask.js:48
|
||||
#: pretix/static/pretixbase/js/asynctask.js:125
|
||||
msgid "Your request has been queued on the server and will soon be processed."
|
||||
msgstr "طلبك قيد الانتظار وستتم معالجته قريبا."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:69
|
||||
#: pretix/static/pretixbase/js/asynctask.js:146
|
||||
#: pretix/static/pretixbase/js/asynctask.js:54
|
||||
#: pretix/static/pretixbase/js/asynctask.js:131
|
||||
msgid ""
|
||||
"Your request arrived on the server but we still wait for it to be processed. "
|
||||
"If this takes longer than two minutes, please contact us or go back in your "
|
||||
@@ -355,36 +354,36 @@ msgstr ""
|
||||
"وصل طلبك للخادم وننتظر تنفيذه. إذا استغرق الأمر أكثر من دقيقتين تواصل معنا "
|
||||
"أو عاود المحاولة مجددا."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:105
|
||||
#: pretix/static/pretixbase/js/asynctask.js:193
|
||||
#: pretix/static/pretixbase/js/asynctask.js:198
|
||||
#: pretix/static/pretixbase/js/asynctask.js:90
|
||||
#: pretix/static/pretixbase/js/asynctask.js:178
|
||||
#: pretix/static/pretixbase/js/asynctask.js:183
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:24
|
||||
msgid "An error of type {code} occurred."
|
||||
msgstr "حدث خطأ من نوع {code}."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:108
|
||||
#: pretix/static/pretixbase/js/asynctask.js:93
|
||||
msgid ""
|
||||
"We currently cannot reach the server, but we keep trying. Last error code: "
|
||||
"{code}"
|
||||
msgstr "لم نتمكن من الاتصال بالخادم، لكن سنواصل المحاولة، رمز آخر خطأ: {code}"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:160
|
||||
#: pretix/static/pretixbase/js/asynctask.js:145
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:21
|
||||
msgid "The request took too long. Please try again."
|
||||
msgstr "استغرقت الطلب فترة طويلة، الرجاء المحاولة مرة أخرى."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:201
|
||||
#: pretix/static/pretixbase/js/asynctask.js:186
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:26
|
||||
msgid ""
|
||||
"We currently cannot reach the server. Please try again. Error code: {code}"
|
||||
msgstr ""
|
||||
"لا يمكننا الوصول إلى الخادم حاليا، حاول مرة أخرى من فضلك. رمز الخطأ : {code}"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:230
|
||||
#: pretix/static/pretixbase/js/asynctask.js:215
|
||||
msgid "We are processing your request …"
|
||||
msgstr "جاري معالجة طلبك …"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:238
|
||||
#: pretix/static/pretixbase/js/asynctask.js:223
|
||||
msgid ""
|
||||
"We are currently sending your request to the server. If this takes longer "
|
||||
"than one minute, please check your internet connection and then reload this "
|
||||
@@ -393,7 +392,7 @@ msgstr ""
|
||||
"نعمل الآن على ارسال طلبك إلى الخادم، إذا أستغرقت العملية أكثر من دقيقة، يرجى "
|
||||
"التحقق من اتصالك بالإنترنت ثم أعد تحميل الصفحة مرة أخرى."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:301
|
||||
#: pretix/static/pretixbase/js/asynctask.js:285
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:71
|
||||
msgid "Close message"
|
||||
msgstr "أغلق الرسالة"
|
||||
@@ -560,46 +559,46 @@ msgstr "توليد الرسائل …"
|
||||
msgid "Unknown error."
|
||||
msgstr "خطأ غير معروف."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:311
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:309
|
||||
msgid "Your color has great contrast and is very easy to read!"
|
||||
msgstr "اللون يتمتع بتباين كبير وتسهل قراءته!"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:315
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:313
|
||||
msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||
msgstr "اللون يحظى بتباين معقول ويمكن أن يكون مناسب للقراءة!"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:319
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:317
|
||||
msgid ""
|
||||
"Your color has bad contrast for text on white background, please choose a "
|
||||
"darker shade."
|
||||
msgstr "تباين اللون سيئ للخلفية البيضاء، الرجاء اختيار لون غامق."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:468
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:488
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:466
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||
msgid "Search query"
|
||||
msgstr "البحث في الاستفسارات"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:484
|
||||
msgid "All"
|
||||
msgstr "الكل"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:487
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:485
|
||||
msgid "None"
|
||||
msgstr "لا شيء"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:491
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:489
|
||||
msgid "Selected only"
|
||||
msgstr "المختارة فقط"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:892
|
||||
msgid "Use a different name internally"
|
||||
msgstr "قم باستخدم اسم مختلف داخليا"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:934
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:932
|
||||
msgid "Click to close"
|
||||
msgstr "اضغط لاغلاق الصفحة"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1009
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1007
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "لم تقم بحفظ التعديلات!"
|
||||
|
||||
@@ -673,27 +672,23 @@ msgstr "ستسترد %(currency)%(amount)"
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr "الرجاء إدخال المبلغ الذي يمكن للمنظم الاحتفاظ به."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:412
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:436
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "الرجاء إدخال عدد لأحد أنواع التذاكر."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:448
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:472
|
||||
msgid "required"
|
||||
msgstr "مطلوب"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:570
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:575
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:594
|
||||
msgid "Time zone:"
|
||||
msgstr "المنطقة الزمنية:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:561
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:585
|
||||
msgid "Your local time:"
|
||||
msgstr "التوقيت المحلي:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/walletdetection.js:39
|
||||
msgid "Google Pay"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
||||
msgctxt "widget"
|
||||
msgid "Quantity"
|
||||
|
||||
+1922
-2293
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-07-21 11:46+0000\n"
|
||||
"POT-Creation-Date: 2023-06-16 14:35+0000\n"
|
||||
"PO-Revision-Date: 2020-12-19 07:00+0000\n"
|
||||
"Last-Translator: albert <albert.serra.monner@gmail.com>\n"
|
||||
"Language-Team: Catalan <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
@@ -38,7 +38,6 @@ msgid "Venmo"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
||||
#: pretix/static/pretixpresale/js/walletdetection.js:38
|
||||
msgid "Apple Pay"
|
||||
msgstr ""
|
||||
|
||||
@@ -136,8 +135,8 @@ msgid "Continue"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:204
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:235
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:157
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:188
|
||||
msgid "Confirming your payment …"
|
||||
msgstr ""
|
||||
|
||||
@@ -159,15 +158,15 @@ msgstr ""
|
||||
msgid "Total revenue"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:13
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:12
|
||||
msgid "Contacting Stripe …"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:70
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:65
|
||||
msgid "Total"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:211
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:164
|
||||
msgid "Contacting your bank …"
|
||||
msgstr ""
|
||||
|
||||
@@ -317,62 +316,62 @@ msgstr ""
|
||||
msgid "close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:58
|
||||
#: pretix/static/pretixbase/js/asynctask.js:135
|
||||
#: pretix/static/pretixbase/js/asynctask.js:43
|
||||
#: pretix/static/pretixbase/js/asynctask.js:120
|
||||
msgid ""
|
||||
"Your request is currently being processed. Depending on the size of your "
|
||||
"event, this might take up to a few minutes."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:63
|
||||
#: pretix/static/pretixbase/js/asynctask.js:140
|
||||
#: pretix/static/pretixbase/js/asynctask.js:48
|
||||
#: pretix/static/pretixbase/js/asynctask.js:125
|
||||
msgid "Your request has been queued on the server and will soon be processed."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:69
|
||||
#: pretix/static/pretixbase/js/asynctask.js:146
|
||||
#: pretix/static/pretixbase/js/asynctask.js:54
|
||||
#: pretix/static/pretixbase/js/asynctask.js:131
|
||||
msgid ""
|
||||
"Your request arrived on the server but we still wait for it to be processed. "
|
||||
"If this takes longer than two minutes, please contact us or go back in your "
|
||||
"browser and try again."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:105
|
||||
#: pretix/static/pretixbase/js/asynctask.js:193
|
||||
#: pretix/static/pretixbase/js/asynctask.js:198
|
||||
#: pretix/static/pretixbase/js/asynctask.js:90
|
||||
#: pretix/static/pretixbase/js/asynctask.js:178
|
||||
#: pretix/static/pretixbase/js/asynctask.js:183
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:24
|
||||
msgid "An error of type {code} occurred."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:108
|
||||
#: pretix/static/pretixbase/js/asynctask.js:93
|
||||
msgid ""
|
||||
"We currently cannot reach the server, but we keep trying. Last error code: "
|
||||
"{code}"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:160
|
||||
#: pretix/static/pretixbase/js/asynctask.js:145
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:21
|
||||
msgid "The request took too long. Please try again."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:201
|
||||
#: pretix/static/pretixbase/js/asynctask.js:186
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:26
|
||||
msgid ""
|
||||
"We currently cannot reach the server. Please try again. Error code: {code}"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:230
|
||||
#: pretix/static/pretixbase/js/asynctask.js:215
|
||||
msgid "We are processing your request …"
|
||||
msgstr "Estem processant la vostra sol·licitud …"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:238
|
||||
#: pretix/static/pretixbase/js/asynctask.js:223
|
||||
msgid ""
|
||||
"We are currently sending your request to the server. If this takes longer "
|
||||
"than one minute, please check your internet connection and then reload this "
|
||||
"page and try again."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:301
|
||||
#: pretix/static/pretixbase/js/asynctask.js:285
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:71
|
||||
msgid "Close message"
|
||||
msgstr ""
|
||||
@@ -539,46 +538,46 @@ msgstr ""
|
||||
msgid "Unknown error."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:311
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:309
|
||||
msgid "Your color has great contrast and is very easy to read!"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:315
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:313
|
||||
msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:319
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:317
|
||||
msgid ""
|
||||
"Your color has bad contrast for text on white background, please choose a "
|
||||
"darker shade."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:468
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:488
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:466
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||
msgid "Search query"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:484
|
||||
msgid "All"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:487
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:485
|
||||
msgid "None"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:491
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:489
|
||||
msgid "Selected only"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:892
|
||||
msgid "Use a different name internally"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:934
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:932
|
||||
msgid "Click to close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1009
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1007
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr ""
|
||||
|
||||
@@ -642,29 +641,25 @@ msgstr ""
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:412
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:436
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:448
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:472
|
||||
#, fuzzy
|
||||
#| msgid "Cart expired"
|
||||
msgid "required"
|
||||
msgstr "Cistella expirada"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:570
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:575
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:594
|
||||
msgid "Time zone:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:561
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:585
|
||||
msgid "Your local time:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/walletdetection.js:39
|
||||
msgid "Google Pay"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
||||
msgctxt "widget"
|
||||
msgid "Quantity"
|
||||
|
||||
+1923
-2317
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-07-21 11:46+0000\n"
|
||||
"POT-Creation-Date: 2023-06-16 14:35+0000\n"
|
||||
"PO-Revision-Date: 2023-03-19 06:00+0000\n"
|
||||
"Last-Translator: Michael <michael.happl@gmx.at>\n"
|
||||
"Language-Team: Czech <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
@@ -38,7 +38,6 @@ msgid "Venmo"
|
||||
msgstr "Venmo"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
||||
#: pretix/static/pretixpresale/js/walletdetection.js:38
|
||||
msgid "Apple Pay"
|
||||
msgstr "Apple Pay"
|
||||
|
||||
@@ -136,8 +135,8 @@ msgid "Continue"
|
||||
msgstr "Pokračovat"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:204
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:235
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:157
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:188
|
||||
msgid "Confirming your payment …"
|
||||
msgstr "Potvrzuji vaši platbu …"
|
||||
|
||||
@@ -159,15 +158,15 @@ msgstr "Zaplacené objednávky"
|
||||
msgid "Total revenue"
|
||||
msgstr "Celkové příjmy"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:13
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:12
|
||||
msgid "Contacting Stripe …"
|
||||
msgstr "Kontaktuji Stripe …"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:70
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:65
|
||||
msgid "Total"
|
||||
msgstr "Celkem"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:211
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:164
|
||||
msgid "Contacting your bank …"
|
||||
msgstr "Kontaktuji vaši banku …"
|
||||
|
||||
@@ -317,8 +316,8 @@ msgstr "Aktuálně uvnitř"
|
||||
msgid "close"
|
||||
msgstr "zavřít"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:58
|
||||
#: pretix/static/pretixbase/js/asynctask.js:135
|
||||
#: pretix/static/pretixbase/js/asynctask.js:43
|
||||
#: pretix/static/pretixbase/js/asynctask.js:120
|
||||
msgid ""
|
||||
"Your request is currently being processed. Depending on the size of your "
|
||||
"event, this might take up to a few minutes."
|
||||
@@ -326,13 +325,13 @@ msgstr ""
|
||||
"Váš požadavek je právě zpracováván. V závislosti na velikosti vaší udalosti, "
|
||||
"to může trvat několik minut."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:63
|
||||
#: pretix/static/pretixbase/js/asynctask.js:140
|
||||
#: pretix/static/pretixbase/js/asynctask.js:48
|
||||
#: pretix/static/pretixbase/js/asynctask.js:125
|
||||
msgid "Your request has been queued on the server and will soon be processed."
|
||||
msgstr "Váš požadavek byl vložem do fronty serveru a brzy bude zpracován."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:69
|
||||
#: pretix/static/pretixbase/js/asynctask.js:146
|
||||
#: pretix/static/pretixbase/js/asynctask.js:54
|
||||
#: pretix/static/pretixbase/js/asynctask.js:131
|
||||
msgid ""
|
||||
"Your request arrived on the server but we still wait for it to be processed. "
|
||||
"If this takes longer than two minutes, please contact us or go back in your "
|
||||
@@ -342,14 +341,14 @@ msgstr ""
|
||||
"Pokud to trvá více jak dvě minuty, prosím kontaktuje nás nebo se vraťte do "
|
||||
"vašeho prohlížeče a zkuste to znovu."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:105
|
||||
#: pretix/static/pretixbase/js/asynctask.js:193
|
||||
#: pretix/static/pretixbase/js/asynctask.js:198
|
||||
#: pretix/static/pretixbase/js/asynctask.js:90
|
||||
#: pretix/static/pretixbase/js/asynctask.js:178
|
||||
#: pretix/static/pretixbase/js/asynctask.js:183
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:24
|
||||
msgid "An error of type {code} occurred."
|
||||
msgstr "Vyskytla se chyba {code}."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:108
|
||||
#: pretix/static/pretixbase/js/asynctask.js:93
|
||||
msgid ""
|
||||
"We currently cannot reach the server, but we keep trying. Last error code: "
|
||||
"{code}"
|
||||
@@ -357,12 +356,12 @@ msgstr ""
|
||||
"Momentálně nemůžeme kontaktovat server, ale stále se o to pokoušíme. "
|
||||
"Poslední chybový kód: {code}"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:160
|
||||
#: pretix/static/pretixbase/js/asynctask.js:145
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:21
|
||||
msgid "The request took too long. Please try again."
|
||||
msgstr "Zpracování požadavku trvá příliš dlouho. Prosím zkuste to znovu."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:201
|
||||
#: pretix/static/pretixbase/js/asynctask.js:186
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:26
|
||||
msgid ""
|
||||
"We currently cannot reach the server. Please try again. Error code: {code}"
|
||||
@@ -370,11 +369,11 @@ msgstr ""
|
||||
"Momentálně nemůžeme kontaktovat server. Prosím zkuste to znovu. Chybový kód: "
|
||||
"{code}"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:230
|
||||
#: pretix/static/pretixbase/js/asynctask.js:215
|
||||
msgid "We are processing your request …"
|
||||
msgstr "Zpracováváme váš požadavek …"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:238
|
||||
#: pretix/static/pretixbase/js/asynctask.js:223
|
||||
msgid ""
|
||||
"We are currently sending your request to the server. If this takes longer "
|
||||
"than one minute, please check your internet connection and then reload this "
|
||||
@@ -384,7 +383,7 @@ msgstr ""
|
||||
"prosím zkontrolujte své internetové připojení a znovu načtěte stránku a "
|
||||
"zkuste to znovu."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:301
|
||||
#: pretix/static/pretixbase/js/asynctask.js:285
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:71
|
||||
msgid "Close message"
|
||||
msgstr "Zavřít zprávu"
|
||||
@@ -551,16 +550,16 @@ msgstr "Vytváření zpráv…"
|
||||
msgid "Unknown error."
|
||||
msgstr "Neznámá chyba."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:311
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:309
|
||||
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á!"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:315
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:313
|
||||
msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||
msgstr ""
|
||||
"Tato barva má slušný kontrast a pravděpodobně je dostatečně dobře čitelná!"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:319
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:317
|
||||
msgid ""
|
||||
"Your color has bad contrast for text on white background, please choose a "
|
||||
"darker shade."
|
||||
@@ -568,32 +567,32 @@ msgstr ""
|
||||
"Tato barva je pro text na bílém pozadí špatně kontrastní, zvolte prosím "
|
||||
"tmavší odstín."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:468
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:488
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:466
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||
msgid "Search query"
|
||||
msgstr "Hledaný výraz"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:484
|
||||
msgid "All"
|
||||
msgstr "Všechny"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:487
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:485
|
||||
msgid "None"
|
||||
msgstr "Žádný"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:491
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:489
|
||||
msgid "Selected only"
|
||||
msgstr "Pouze vybrané"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:892
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Interně používat jiný název"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:934
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:932
|
||||
msgid "Click to close"
|
||||
msgstr "Kliknutím zavřete"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1009
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1007
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Máte neuložené změny!"
|
||||
|
||||
@@ -660,29 +659,23 @@ msgstr "Dostanete %(currency)s %(amount)s zpět"
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr "Zadejte částku, kterou si organizátor může ponechat."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:412
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:436
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "Zadejte prosím množství pro jeden z typů vstupenek."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:448
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:472
|
||||
msgid "required"
|
||||
msgstr "povinný"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:570
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:575
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:594
|
||||
msgid "Time zone:"
|
||||
msgstr "Časové pásmo:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:561
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:585
|
||||
msgid "Your local time:"
|
||||
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
|
||||
msgctxt "widget"
|
||||
msgid "Quantity"
|
||||
|
||||
+1920
-2276
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-07-21 11:46+0000\n"
|
||||
"POT-Creation-Date: 2023-06-16 14:35+0000\n"
|
||||
"PO-Revision-Date: 2022-12-01 17:00+0000\n"
|
||||
"Last-Translator: Mie Frydensbjerg <mif@aarhus.dk>\n"
|
||||
"Language-Team: Danish <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
@@ -37,7 +37,6 @@ msgid "Venmo"
|
||||
msgstr "Venmo"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
||||
#: pretix/static/pretixpresale/js/walletdetection.js:38
|
||||
msgid "Apple Pay"
|
||||
msgstr "Apple Pay"
|
||||
|
||||
@@ -140,8 +139,8 @@ msgid "Continue"
|
||||
msgstr "Fortsæt"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:204
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:235
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:157
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:188
|
||||
msgid "Confirming your payment …"
|
||||
msgstr "Bekræfter din betaling …"
|
||||
|
||||
@@ -163,15 +162,15 @@ msgstr "Betalte bestillinger"
|
||||
msgid "Total revenue"
|
||||
msgstr "Omsætning i alt"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:13
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:12
|
||||
msgid "Contacting Stripe …"
|
||||
msgstr "Kontakter Stripe …"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:70
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:65
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:211
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:164
|
||||
msgid "Contacting your bank …"
|
||||
msgstr "Kontakter din bank …"
|
||||
|
||||
@@ -329,8 +328,8 @@ msgstr "Inde i øjeblikket"
|
||||
msgid "close"
|
||||
msgstr "Luk"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:58
|
||||
#: pretix/static/pretixbase/js/asynctask.js:135
|
||||
#: pretix/static/pretixbase/js/asynctask.js:43
|
||||
#: pretix/static/pretixbase/js/asynctask.js:120
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "Your request has been queued on the server and will now be processed. "
|
||||
@@ -342,8 +341,8 @@ msgstr ""
|
||||
"Din forespørgsel er under behandling. Alt efter størrelsen af din event kan "
|
||||
"der gå op til et par minutter."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:63
|
||||
#: pretix/static/pretixbase/js/asynctask.js:140
|
||||
#: pretix/static/pretixbase/js/asynctask.js:48
|
||||
#: pretix/static/pretixbase/js/asynctask.js:125
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "Your request has been queued on the server and will now be processed. "
|
||||
@@ -353,8 +352,8 @@ msgstr ""
|
||||
"Din forespørgsel er under behandling. Alt efter størrelsen af din event kan "
|
||||
"der gå op til et par minutter."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:69
|
||||
#: pretix/static/pretixbase/js/asynctask.js:146
|
||||
#: pretix/static/pretixbase/js/asynctask.js:54
|
||||
#: pretix/static/pretixbase/js/asynctask.js:131
|
||||
msgid ""
|
||||
"Your request arrived on the server but we still wait for it to be processed. "
|
||||
"If this takes longer than two minutes, please contact us or go back in your "
|
||||
@@ -363,14 +362,14 @@ msgstr ""
|
||||
"Din forespørgsel er under behandling. Hvis der går mere end to minutter, så "
|
||||
"kontakt os eller gå tilbage og prøv igen."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:105
|
||||
#: pretix/static/pretixbase/js/asynctask.js:193
|
||||
#: pretix/static/pretixbase/js/asynctask.js:198
|
||||
#: pretix/static/pretixbase/js/asynctask.js:90
|
||||
#: pretix/static/pretixbase/js/asynctask.js:178
|
||||
#: pretix/static/pretixbase/js/asynctask.js:183
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:24
|
||||
msgid "An error of type {code} occurred."
|
||||
msgstr "Der er sket en fejl ({code})."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:108
|
||||
#: pretix/static/pretixbase/js/asynctask.js:93
|
||||
msgid ""
|
||||
"We currently cannot reach the server, but we keep trying. Last error code: "
|
||||
"{code}"
|
||||
@@ -378,12 +377,12 @@ msgstr ""
|
||||
"Vi kan ikke komme i kontakt med serveren, men prøver igen. Seneste fejlkode: "
|
||||
"{code}"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:160
|
||||
#: pretix/static/pretixbase/js/asynctask.js:145
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:21
|
||||
msgid "The request took too long. Please try again."
|
||||
msgstr "Forespørgslen tog for lang tid. Prøv igen."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:201
|
||||
#: pretix/static/pretixbase/js/asynctask.js:186
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:26
|
||||
msgid ""
|
||||
"We currently cannot reach the server. Please try again. Error code: {code}"
|
||||
@@ -391,11 +390,11 @@ msgstr ""
|
||||
"Vi kan i øjeblikket ikke komme i kontakt med serveren. Prøv igen. Fejlkode: "
|
||||
"{code}"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:230
|
||||
#: pretix/static/pretixbase/js/asynctask.js:215
|
||||
msgid "We are processing your request …"
|
||||
msgstr "Vi behandler din bestilling …"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:238
|
||||
#: pretix/static/pretixbase/js/asynctask.js:223
|
||||
msgid ""
|
||||
"We are currently sending your request to the server. If this takes longer "
|
||||
"than one minute, please check your internet connection and then reload this "
|
||||
@@ -404,7 +403,7 @@ msgstr ""
|
||||
"Din forespørgsel bliver sendt til serveren. Hvis det tager mere end et "
|
||||
"minut, så tjek din internetforbindelse, genindlæs siden og prøv igen."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:301
|
||||
#: pretix/static/pretixbase/js/asynctask.js:285
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:71
|
||||
msgid "Close message"
|
||||
msgstr "Luk besked"
|
||||
@@ -574,46 +573,46 @@ msgstr "Opretter beskeder …"
|
||||
msgid "Unknown error."
|
||||
msgstr "Ukendt fejl."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:311
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:309
|
||||
msgid "Your color has great contrast and is very easy to read!"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:315
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:313
|
||||
msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:319
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:317
|
||||
msgid ""
|
||||
"Your color has bad contrast for text on white background, please choose a "
|
||||
"darker shade."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:468
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:488
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:466
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||
msgid "Search query"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:484
|
||||
msgid "All"
|
||||
msgstr "Alle"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:487
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:485
|
||||
msgid "None"
|
||||
msgstr "Ingen"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:491
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:489
|
||||
msgid "Selected only"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:892
|
||||
msgid "Use a different name internally"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:934
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:932
|
||||
msgid "Click to close"
|
||||
msgstr "Klik for at lukke"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1009
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1007
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Du har ændringer, der ikke er gemt!"
|
||||
|
||||
@@ -687,31 +686,25 @@ msgstr "fra %(currency)s %(price)s"
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:412
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:436
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:448
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:472
|
||||
#, fuzzy
|
||||
#| msgid "Cart expired"
|
||||
msgid "required"
|
||||
msgstr "Kurv udløbet"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:570
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:575
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:594
|
||||
msgid "Time zone:"
|
||||
msgstr "Tidszone:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:561
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:585
|
||||
msgid "Your local time:"
|
||||
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
|
||||
msgctxt "widget"
|
||||
msgid "Quantity"
|
||||
|
||||
+2048
-2366
File diff suppressed because it is too large
Load Diff
@@ -7,8 +7,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-07-21 11:46+0000\n"
|
||||
"PO-Revision-Date: 2023-07-21 12:47+0000\n"
|
||||
"POT-Creation-Date: 2023-06-16 14:35+0000\n"
|
||||
"PO-Revision-Date: 2023-02-09 13:56+0000\n"
|
||||
"Last-Translator: Raphael Michel <michel@rami.io>\n"
|
||||
"Language-Team: German <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
"de/>\n"
|
||||
@@ -17,7 +17,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 4.17\n"
|
||||
"X-Generator: Weblate 4.15.2\n"
|
||||
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
|
||||
@@ -38,7 +38,6 @@ msgid "Venmo"
|
||||
msgstr "Venmo"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
||||
#: pretix/static/pretixpresale/js/walletdetection.js:38
|
||||
msgid "Apple Pay"
|
||||
msgstr "Apple Pay"
|
||||
|
||||
@@ -136,8 +135,8 @@ msgid "Continue"
|
||||
msgstr "Fortfahren"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:204
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:235
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:157
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:188
|
||||
msgid "Confirming your payment …"
|
||||
msgstr "Zahlung wird bestätigt …"
|
||||
|
||||
@@ -159,15 +158,15 @@ msgstr "Bezahlte Bestellungen"
|
||||
msgid "Total revenue"
|
||||
msgstr "Gesamtumsatz"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:13
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:12
|
||||
msgid "Contacting Stripe …"
|
||||
msgstr "Kontaktiere Stripe …"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:70
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:65
|
||||
msgid "Total"
|
||||
msgstr "Gesamt"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:211
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:164
|
||||
msgid "Contacting your bank …"
|
||||
msgstr "Kontaktiere Ihre Bank …"
|
||||
|
||||
@@ -251,7 +250,7 @@ msgstr "Dieses Ticket ist noch nicht bezahlt. Möchten Sie dennoch fortfahren?"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:51
|
||||
msgid "Additional information required"
|
||||
msgstr "Zusätzliche Informationen benötigt"
|
||||
msgstr "Weitere Informationen benötigt"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:52
|
||||
msgid "Valid ticket"
|
||||
@@ -317,8 +316,8 @@ msgstr "Derzeit anwesend"
|
||||
msgid "close"
|
||||
msgstr "schließen"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:58
|
||||
#: pretix/static/pretixbase/js/asynctask.js:135
|
||||
#: pretix/static/pretixbase/js/asynctask.js:43
|
||||
#: pretix/static/pretixbase/js/asynctask.js:120
|
||||
msgid ""
|
||||
"Your request is currently being processed. Depending on the size of your "
|
||||
"event, this might take up to a few minutes."
|
||||
@@ -326,15 +325,15 @@ msgstr ""
|
||||
"Ihre Anfrage wird nun verarbeitet. Je nach Größe der Veranstaltung kann dies "
|
||||
"einige Minuten dauern."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:63
|
||||
#: pretix/static/pretixbase/js/asynctask.js:140
|
||||
#: pretix/static/pretixbase/js/asynctask.js:48
|
||||
#: pretix/static/pretixbase/js/asynctask.js:125
|
||||
msgid "Your request has been queued on the server and will soon be processed."
|
||||
msgstr ""
|
||||
"Ihre Anfrage befindet sich beim Server in der Warteschlange und wird bald "
|
||||
"verarbeitet."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:69
|
||||
#: pretix/static/pretixbase/js/asynctask.js:146
|
||||
#: pretix/static/pretixbase/js/asynctask.js:54
|
||||
#: pretix/static/pretixbase/js/asynctask.js:131
|
||||
msgid ""
|
||||
"Your request arrived on the server but we still wait for it to be processed. "
|
||||
"If this takes longer than two minutes, please contact us or go back in your "
|
||||
@@ -345,14 +344,14 @@ msgstr ""
|
||||
"bitte oder gehen Sie in Ihrem Browser einen Schritt zurück und versuchen es "
|
||||
"erneut."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:105
|
||||
#: pretix/static/pretixbase/js/asynctask.js:193
|
||||
#: pretix/static/pretixbase/js/asynctask.js:198
|
||||
#: pretix/static/pretixbase/js/asynctask.js:90
|
||||
#: pretix/static/pretixbase/js/asynctask.js:178
|
||||
#: pretix/static/pretixbase/js/asynctask.js:183
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:24
|
||||
msgid "An error of type {code} occurred."
|
||||
msgstr "Ein Fehler ist aufgetreten. Fehlercode: {code}"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:108
|
||||
#: pretix/static/pretixbase/js/asynctask.js:93
|
||||
msgid ""
|
||||
"We currently cannot reach the server, but we keep trying. Last error code: "
|
||||
"{code}"
|
||||
@@ -360,12 +359,12 @@ msgstr ""
|
||||
"Wir können den Server aktuell nicht erreichen, versuchen es aber weiter. "
|
||||
"Letzter Fehlercode: {code}"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:160
|
||||
#: pretix/static/pretixbase/js/asynctask.js:145
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:21
|
||||
msgid "The request took too long. Please try again."
|
||||
msgstr "Diese Anfrage hat zu lange gedauert. Bitte erneut versuchen."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:201
|
||||
#: pretix/static/pretixbase/js/asynctask.js:186
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:26
|
||||
msgid ""
|
||||
"We currently cannot reach the server. Please try again. Error code: {code}"
|
||||
@@ -373,11 +372,11 @@ msgstr ""
|
||||
"Wir können den Server aktuell nicht erreichen. Bitte versuchen Sie es noch "
|
||||
"einmal. Fehlercode: {code}"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:230
|
||||
#: pretix/static/pretixbase/js/asynctask.js:215
|
||||
msgid "We are processing your request …"
|
||||
msgstr "Wir verarbeiten Ihre Anfrage …"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:238
|
||||
#: pretix/static/pretixbase/js/asynctask.js:223
|
||||
msgid ""
|
||||
"We are currently sending your request to the server. If this takes longer "
|
||||
"than one minute, please check your internet connection and then reload this "
|
||||
@@ -387,7 +386,7 @@ msgstr ""
|
||||
"dauert, prüfen Sie bitte Ihre Internetverbindung. Danach können Sie diese "
|
||||
"Seite neu laden und es erneut versuchen."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:301
|
||||
#: pretix/static/pretixbase/js/asynctask.js:285
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:71
|
||||
msgid "Close message"
|
||||
msgstr "Schließen"
|
||||
@@ -491,7 +490,7 @@ msgstr "Minuten"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:128
|
||||
msgid "Duplicate"
|
||||
msgstr "Duplizieren"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:72
|
||||
msgid "Check-in QR"
|
||||
@@ -556,17 +555,17 @@ msgstr "Generiere Nachrichten…"
|
||||
msgid "Unknown error."
|
||||
msgstr "Unbekannter Fehler."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:311
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:309
|
||||
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!"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:315
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:313
|
||||
msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||
msgstr ""
|
||||
"Diese Farbe hat einen ausreichenden Kontrast und ist wahrscheinlich gut zu "
|
||||
"lesen!"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:319
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:317
|
||||
msgid ""
|
||||
"Your color has bad contrast for text on white background, please choose a "
|
||||
"darker shade."
|
||||
@@ -574,32 +573,32 @@ msgstr ""
|
||||
"Diese Farbe hat einen schlechten Kontrast für Text auf einem weißen "
|
||||
"Hintergrund. Bitte wählen Sie eine dunklere Farbe."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:468
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:488
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:466
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||
msgid "Search query"
|
||||
msgstr "Suchbegriff"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:484
|
||||
msgid "All"
|
||||
msgstr "Alle"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:487
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:485
|
||||
msgid "None"
|
||||
msgstr "Keine"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:491
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:489
|
||||
msgid "Selected only"
|
||||
msgstr "Nur ausgewählte"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:892
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Intern einen anderen Namen verwenden"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:934
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:932
|
||||
msgid "Click to close"
|
||||
msgstr "Klicken zum Schließen"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1009
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1007
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Sie haben ungespeicherte Änderungen!"
|
||||
|
||||
@@ -664,60 +663,57 @@ msgstr "Sie erhalten %(currency)s %(amount)s zurück"
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr "Bitte geben Sie den Betrag ein, den der Veranstalter einbehalten darf."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:412
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:436
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "Bitte tragen Sie eine Menge für eines der Produkte ein."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:448
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:472
|
||||
msgid "required"
|
||||
msgstr "verpflichtend"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:570
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:575
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:594
|
||||
msgid "Time zone:"
|
||||
msgstr "Zeitzone:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:561
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:585
|
||||
msgid "Your local time:"
|
||||
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
|
||||
msgctxt "widget"
|
||||
msgid "Quantity"
|
||||
msgstr "Menge"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:18
|
||||
msgctxt "widget"
|
||||
msgid "Decrease quantity"
|
||||
msgstr "Menge reduzieren"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:19
|
||||
msgctxt "widget"
|
||||
msgid "Increase quantity"
|
||||
msgstr "Menge erhöhen"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:20
|
||||
msgctxt "widget"
|
||||
msgid "Price"
|
||||
msgstr "Preis"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:21
|
||||
#, javascript-format
|
||||
#, fuzzy, javascript-format
|
||||
#| msgid "Selected only"
|
||||
msgctxt "widget"
|
||||
msgid "Select %s"
|
||||
msgstr "%s auswählen"
|
||||
msgstr "Nur ausgewählte"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:22
|
||||
#, javascript-format
|
||||
#, fuzzy, javascript-format
|
||||
#| msgctxt "widget"
|
||||
#| msgid "See variations"
|
||||
msgctxt "widget"
|
||||
msgid "Select variant %s"
|
||||
msgstr "Variante %s auswählen"
|
||||
msgstr "Varianten zeigen"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:23
|
||||
msgctxt "widget"
|
||||
@@ -921,9 +917,6 @@ msgid ""
|
||||
"add yourself to the waiting list. We will then notify if seats are available "
|
||||
"again."
|
||||
msgstr ""
|
||||
"Manche oder alle Ticketkategorien sind derzeit ausverkauft. Wenn Sie "
|
||||
"möchten, können Sie sich in die Warteliste eintragen. Wir benachrichtigen "
|
||||
"Sie dann, wenn die gewünschten Plätze wieder verfügbar sind."
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:63
|
||||
msgctxt "widget"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
2FA
|
||||
ABN
|
||||
AES
|
||||
Absenderadresse
|
||||
Absenderinformation
|
||||
Absendername
|
||||
@@ -15,7 +14,6 @@ Apps
|
||||
APM
|
||||
as
|
||||
Aufteilungsliste
|
||||
aufzubuchen
|
||||
auschecken
|
||||
Ausgangsscan
|
||||
Ausgangsscans
|
||||
@@ -75,7 +73,6 @@ Cronjob
|
||||
csv
|
||||
Customer
|
||||
CZK
|
||||
Dashboard
|
||||
Debug
|
||||
dekodi
|
||||
deliverability
|
||||
@@ -169,7 +166,6 @@ landesspezifische
|
||||
Lead
|
||||
Leaflet
|
||||
Linktext
|
||||
Lösch
|
||||
loszulegen
|
||||
Ltd
|
||||
max
|
||||
@@ -180,7 +176,6 @@ Merchandise
|
||||
Meta
|
||||
Metadaten
|
||||
Mi
|
||||
Mifare
|
||||
Mitgliedschafts
|
||||
Mitgliedschaftsdauer
|
||||
min
|
||||
@@ -194,7 +189,6 @@ NFC
|
||||
Nr
|
||||
NREI
|
||||
number
|
||||
NXP
|
||||
OK
|
||||
On
|
||||
Open
|
||||
@@ -219,7 +213,6 @@ Plugins
|
||||
POS
|
||||
Postfix
|
||||
PostgreSQL
|
||||
PPRO
|
||||
prefix
|
||||
pretix
|
||||
pretixdesk
|
||||
@@ -318,7 +311,6 @@ txt
|
||||
überzahlte
|
||||
uhrzeit
|
||||
UID
|
||||
Ultralight
|
||||
ungespeicherte
|
||||
unkategorisiert
|
||||
Unkategorisierte
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,8 +7,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-07-21 11:46+0000\n"
|
||||
"PO-Revision-Date: 2023-06-27 14:35+0000\n"
|
||||
"POT-Creation-Date: 2023-06-16 14:35+0000\n"
|
||||
"PO-Revision-Date: 2023-02-09 13:56+0000\n"
|
||||
"Last-Translator: Raphael Michel <michel@rami.io>\n"
|
||||
"Language-Team: German (informal) <https://translate.pretix.eu/projects/"
|
||||
"pretix/pretix-js/de_Informal/>\n"
|
||||
@@ -17,7 +17,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 4.17\n"
|
||||
"X-Generator: Weblate 4.15.2\n"
|
||||
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
|
||||
@@ -38,7 +38,6 @@ msgid "Venmo"
|
||||
msgstr "Venmo"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
||||
#: pretix/static/pretixpresale/js/walletdetection.js:38
|
||||
msgid "Apple Pay"
|
||||
msgstr "Apple Pay"
|
||||
|
||||
@@ -136,8 +135,8 @@ msgid "Continue"
|
||||
msgstr "Fortfahren"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:204
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:235
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:157
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:188
|
||||
msgid "Confirming your payment …"
|
||||
msgstr "Zahlung wird bestätigt …"
|
||||
|
||||
@@ -159,15 +158,15 @@ msgstr "Bezahlte Bestellungen"
|
||||
msgid "Total revenue"
|
||||
msgstr "Gesamtumsatz"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:13
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:12
|
||||
msgid "Contacting Stripe …"
|
||||
msgstr "Kontaktiere Stripe …"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:70
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:65
|
||||
msgid "Total"
|
||||
msgstr "Gesamt"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:211
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:164
|
||||
msgid "Contacting your bank …"
|
||||
msgstr "Kontaktiere deine Bank …"
|
||||
|
||||
@@ -251,7 +250,7 @@ msgstr "Dieses Ticket ist noch nicht bezahlt. Möchtest du dennoch fortfahren?"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:51
|
||||
msgid "Additional information required"
|
||||
msgstr "Zusätzliche Informationen benötigt"
|
||||
msgstr "Weitere Informationen benötigt"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:52
|
||||
msgid "Valid ticket"
|
||||
@@ -317,8 +316,8 @@ msgstr "Derzeit anwesend"
|
||||
msgid "close"
|
||||
msgstr "schließen"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:58
|
||||
#: pretix/static/pretixbase/js/asynctask.js:135
|
||||
#: pretix/static/pretixbase/js/asynctask.js:43
|
||||
#: pretix/static/pretixbase/js/asynctask.js:120
|
||||
msgid ""
|
||||
"Your request is currently being processed. Depending on the size of your "
|
||||
"event, this might take up to a few minutes."
|
||||
@@ -326,15 +325,15 @@ msgstr ""
|
||||
"Deine Anfrage wird nun verarbeitet. Je nach Größe der Veranstaltung kann "
|
||||
"dies einige Minuten dauern."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:63
|
||||
#: pretix/static/pretixbase/js/asynctask.js:140
|
||||
#: pretix/static/pretixbase/js/asynctask.js:48
|
||||
#: pretix/static/pretixbase/js/asynctask.js:125
|
||||
msgid "Your request has been queued on the server and will soon be processed."
|
||||
msgstr ""
|
||||
"Deine Anfrage befindet sich beim Server in der Warteschlange und wird bald "
|
||||
"verarbeitet."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:69
|
||||
#: pretix/static/pretixbase/js/asynctask.js:146
|
||||
#: pretix/static/pretixbase/js/asynctask.js:54
|
||||
#: pretix/static/pretixbase/js/asynctask.js:131
|
||||
msgid ""
|
||||
"Your request arrived on the server but we still wait for it to be processed. "
|
||||
"If this takes longer than two minutes, please contact us or go back in your "
|
||||
@@ -344,14 +343,14 @@ msgstr ""
|
||||
"verarbeitet. Wenn dies länger als zwei Minuten dauert, kontaktiere uns bitte "
|
||||
"oder gehe in deinem Browser einen Schritt zurück und versuche es erneut."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:105
|
||||
#: pretix/static/pretixbase/js/asynctask.js:193
|
||||
#: pretix/static/pretixbase/js/asynctask.js:198
|
||||
#: pretix/static/pretixbase/js/asynctask.js:90
|
||||
#: pretix/static/pretixbase/js/asynctask.js:178
|
||||
#: pretix/static/pretixbase/js/asynctask.js:183
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:24
|
||||
msgid "An error of type {code} occurred."
|
||||
msgstr "Ein Fehler vom Typ {code} ist aufgetreten."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:108
|
||||
#: pretix/static/pretixbase/js/asynctask.js:93
|
||||
msgid ""
|
||||
"We currently cannot reach the server, but we keep trying. Last error code: "
|
||||
"{code}"
|
||||
@@ -359,12 +358,12 @@ msgstr ""
|
||||
"Wir können den Server aktuell nicht erreichen, versuchen es aber weiter. "
|
||||
"Letzter Fehlercode: {code}"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:160
|
||||
#: pretix/static/pretixbase/js/asynctask.js:145
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:21
|
||||
msgid "The request took too long. Please try again."
|
||||
msgstr "Diese Anfrage hat zu lange gedauert. Bitte erneut versuchen."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:201
|
||||
#: pretix/static/pretixbase/js/asynctask.js:186
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:26
|
||||
msgid ""
|
||||
"We currently cannot reach the server. Please try again. Error code: {code}"
|
||||
@@ -372,11 +371,11 @@ msgstr ""
|
||||
"Wir können den Server aktuell nicht erreichen. Bitte versuche es noch "
|
||||
"einmal. Fehlercode: {code}"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:230
|
||||
#: pretix/static/pretixbase/js/asynctask.js:215
|
||||
msgid "We are processing your request …"
|
||||
msgstr "Wir verarbeiten deine Anfrage …"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:238
|
||||
#: pretix/static/pretixbase/js/asynctask.js:223
|
||||
msgid ""
|
||||
"We are currently sending your request to the server. If this takes longer "
|
||||
"than one minute, please check your internet connection and then reload this "
|
||||
@@ -386,7 +385,7 @@ msgstr ""
|
||||
"dauert, prüfe bitte deine Internetverbindung. Danach kannst du diese Seite "
|
||||
"neu laden und es erneut versuchen."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:301
|
||||
#: pretix/static/pretixbase/js/asynctask.js:285
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:71
|
||||
msgid "Close message"
|
||||
msgstr "Schließen"
|
||||
@@ -490,7 +489,7 @@ msgstr "Minuten"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:128
|
||||
msgid "Duplicate"
|
||||
msgstr "Duplizieren"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:72
|
||||
msgid "Check-in QR"
|
||||
@@ -555,17 +554,17 @@ msgstr "Generiere Nachrichten…"
|
||||
msgid "Unknown error."
|
||||
msgstr "Unbekannter Fehler."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:311
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:309
|
||||
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!"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:315
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:313
|
||||
msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||
msgstr ""
|
||||
"Diese Farbe hat einen ausreichenden Kontrast und ist wahrscheinlich gut zu "
|
||||
"lesen!"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:319
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:317
|
||||
msgid ""
|
||||
"Your color has bad contrast for text on white background, please choose a "
|
||||
"darker shade."
|
||||
@@ -573,32 +572,32 @@ msgstr ""
|
||||
"Diese Farbe hat einen schlechten Kontrast für Text auf einem weißen "
|
||||
"Hintergrund. Bitte wähle eine dunklere Farbe."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:468
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:488
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:466
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||
msgid "Search query"
|
||||
msgstr "Suchbegriff"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:484
|
||||
msgid "All"
|
||||
msgstr "Alle"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:487
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:485
|
||||
msgid "None"
|
||||
msgstr "Keine"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:491
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:489
|
||||
msgid "Selected only"
|
||||
msgstr "Nur ausgewählte"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:892
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Intern einen anderen Namen verwenden"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:934
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:932
|
||||
msgid "Click to close"
|
||||
msgstr "Klicken zum Schließen"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1009
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1007
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Du hast ungespeicherte Änderungen!"
|
||||
|
||||
@@ -663,60 +662,57 @@ msgstr "Du erhältst %(currency)s %(amount)s zurück"
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr "Bitte gib den Betrag ein, den der Veranstalter einbehalten darf."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:412
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:436
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "Bitte trage eine Menge für eines der Produkte ein."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:448
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:472
|
||||
msgid "required"
|
||||
msgstr "verpflichtend"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:570
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:575
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:594
|
||||
msgid "Time zone:"
|
||||
msgstr "Zeitzone:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:561
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:585
|
||||
msgid "Your local time:"
|
||||
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
|
||||
msgctxt "widget"
|
||||
msgid "Quantity"
|
||||
msgstr "Menge"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:18
|
||||
msgctxt "widget"
|
||||
msgid "Decrease quantity"
|
||||
msgstr "Menge reduzieren"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:19
|
||||
msgctxt "widget"
|
||||
msgid "Increase quantity"
|
||||
msgstr "Menge erhöhen"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:20
|
||||
msgctxt "widget"
|
||||
msgid "Price"
|
||||
msgstr "Preis"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:21
|
||||
#, javascript-format
|
||||
#, fuzzy, javascript-format
|
||||
#| msgid "Selected only"
|
||||
msgctxt "widget"
|
||||
msgid "Select %s"
|
||||
msgstr "%s auswählen"
|
||||
msgstr "Nur ausgewählte"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:22
|
||||
#, javascript-format
|
||||
#, fuzzy, javascript-format
|
||||
#| msgctxt "widget"
|
||||
#| msgid "See variations"
|
||||
msgctxt "widget"
|
||||
msgid "Select variant %s"
|
||||
msgstr "Variante %s auswählen"
|
||||
msgstr "Varianten zeigen"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:23
|
||||
msgctxt "widget"
|
||||
@@ -920,9 +916,6 @@ msgid ""
|
||||
"add yourself to the waiting list. We will then notify if seats are available "
|
||||
"again."
|
||||
msgstr ""
|
||||
"Manche oder alle Ticketkategorien sind derzeit ausverkauft. Wenn du "
|
||||
"möchtest, kannst du dich in die Warteliste eintragen. Wir benachrichtigen "
|
||||
"dich dann, wenn die gewünschten Plätze wieder verfügbar sind."
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:63
|
||||
msgctxt "widget"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
2FA
|
||||
ABN
|
||||
AES
|
||||
Absenderadresse
|
||||
Absenderinformation
|
||||
Absendername
|
||||
@@ -15,7 +14,6 @@ Apps
|
||||
APM
|
||||
as
|
||||
Aufteilungsliste
|
||||
aufzubuchen
|
||||
auschecken
|
||||
Ausgangsscan
|
||||
Ausgangsscans
|
||||
@@ -75,7 +73,6 @@ Cronjob
|
||||
csv
|
||||
Customer
|
||||
CZK
|
||||
Dashboard
|
||||
Debug
|
||||
dekodi
|
||||
deliverability
|
||||
@@ -169,7 +166,6 @@ landesspezifische
|
||||
Lead
|
||||
Leaflet
|
||||
Linktext
|
||||
Lösch
|
||||
loszulegen
|
||||
Ltd
|
||||
max
|
||||
@@ -180,7 +176,6 @@ Merchandise
|
||||
Meta
|
||||
Metadaten
|
||||
Mi
|
||||
Mifare
|
||||
Mitgliedschafts
|
||||
Mitgliedschaftsdauer
|
||||
min
|
||||
@@ -194,7 +189,6 @@ NFC
|
||||
Nr
|
||||
NREI
|
||||
number
|
||||
NXP
|
||||
OK
|
||||
On
|
||||
Open
|
||||
@@ -219,7 +213,6 @@ Plugins
|
||||
POS
|
||||
Postfix
|
||||
PostgreSQL
|
||||
PPRO
|
||||
prefix
|
||||
pretix
|
||||
pretixdesk
|
||||
@@ -318,7 +311,6 @@ txt
|
||||
überzahlte
|
||||
uhrzeit
|
||||
UID
|
||||
Ultralight
|
||||
ungespeicherte
|
||||
unkategorisiert
|
||||
Unkategorisierte
|
||||
|
||||
+1910
-2187
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-07-27 11:50+0000\n"
|
||||
"POT-Creation-Date: 2023-06-16 14:35+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -37,7 +37,6 @@ msgid "Venmo"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
||||
#: pretix/static/pretixpresale/js/walletdetection.js:38
|
||||
msgid "Apple Pay"
|
||||
msgstr ""
|
||||
|
||||
@@ -135,8 +134,8 @@ msgid "Continue"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:204
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:235
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:157
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:188
|
||||
msgid "Confirming your payment …"
|
||||
msgstr ""
|
||||
|
||||
@@ -158,15 +157,15 @@ msgstr ""
|
||||
msgid "Total revenue"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:13
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:12
|
||||
msgid "Contacting Stripe …"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:70
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:65
|
||||
msgid "Total"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:211
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:164
|
||||
msgid "Contacting your bank …"
|
||||
msgstr ""
|
||||
|
||||
@@ -316,62 +315,62 @@ msgstr ""
|
||||
msgid "close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:58
|
||||
#: pretix/static/pretixbase/js/asynctask.js:135
|
||||
#: pretix/static/pretixbase/js/asynctask.js:43
|
||||
#: pretix/static/pretixbase/js/asynctask.js:120
|
||||
msgid ""
|
||||
"Your request is currently being processed. Depending on the size of your "
|
||||
"event, this might take up to a few minutes."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:63
|
||||
#: pretix/static/pretixbase/js/asynctask.js:140
|
||||
#: pretix/static/pretixbase/js/asynctask.js:48
|
||||
#: pretix/static/pretixbase/js/asynctask.js:125
|
||||
msgid "Your request has been queued on the server and will soon be processed."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:69
|
||||
#: pretix/static/pretixbase/js/asynctask.js:146
|
||||
#: pretix/static/pretixbase/js/asynctask.js:54
|
||||
#: pretix/static/pretixbase/js/asynctask.js:131
|
||||
msgid ""
|
||||
"Your request arrived on the server but we still wait for it to be processed. "
|
||||
"If this takes longer than two minutes, please contact us or go back in your "
|
||||
"browser and try again."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:105
|
||||
#: pretix/static/pretixbase/js/asynctask.js:193
|
||||
#: pretix/static/pretixbase/js/asynctask.js:198
|
||||
#: pretix/static/pretixbase/js/asynctask.js:90
|
||||
#: pretix/static/pretixbase/js/asynctask.js:178
|
||||
#: pretix/static/pretixbase/js/asynctask.js:183
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:24
|
||||
msgid "An error of type {code} occurred."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:108
|
||||
#: pretix/static/pretixbase/js/asynctask.js:93
|
||||
msgid ""
|
||||
"We currently cannot reach the server, but we keep trying. Last error code: "
|
||||
"{code}"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:160
|
||||
#: pretix/static/pretixbase/js/asynctask.js:145
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:21
|
||||
msgid "The request took too long. Please try again."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:201
|
||||
#: pretix/static/pretixbase/js/asynctask.js:186
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:26
|
||||
msgid ""
|
||||
"We currently cannot reach the server. Please try again. Error code: {code}"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:230
|
||||
#: pretix/static/pretixbase/js/asynctask.js:215
|
||||
msgid "We are processing your request …"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:238
|
||||
#: pretix/static/pretixbase/js/asynctask.js:223
|
||||
msgid ""
|
||||
"We are currently sending your request to the server. If this takes longer "
|
||||
"than one minute, please check your internet connection and then reload this "
|
||||
"page and try again."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:301
|
||||
#: pretix/static/pretixbase/js/asynctask.js:285
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:71
|
||||
msgid "Close message"
|
||||
msgstr ""
|
||||
@@ -538,46 +537,46 @@ msgstr ""
|
||||
msgid "Unknown error."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:311
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:309
|
||||
msgid "Your color has great contrast and is very easy to read!"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:315
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:313
|
||||
msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:319
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:317
|
||||
msgid ""
|
||||
"Your color has bad contrast for text on white background, please choose a "
|
||||
"darker shade."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:468
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:488
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:466
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||
msgid "Search query"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:484
|
||||
msgid "All"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:487
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:485
|
||||
msgid "None"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:491
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:489
|
||||
msgid "Selected only"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:892
|
||||
msgid "Use a different name internally"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:934
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:932
|
||||
msgid "Click to close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1009
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1007
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr ""
|
||||
|
||||
@@ -637,27 +636,23 @@ msgstr ""
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:412
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:436
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:448
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:472
|
||||
msgid "required"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:570
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:575
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:594
|
||||
msgid "Time zone:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:561
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:585
|
||||
msgid "Your local time:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/walletdetection.js:39
|
||||
msgid "Google Pay"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
||||
msgctxt "widget"
|
||||
msgid "Quantity"
|
||||
|
||||
+1943
-2332
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-07-21 11:46+0000\n"
|
||||
"POT-Creation-Date: 2023-06-16 14:35+0000\n"
|
||||
"PO-Revision-Date: 2019-10-03 19:00+0000\n"
|
||||
"Last-Translator: Chris Spy <chrispiropoulou@hotmail.com>\n"
|
||||
"Language-Team: Greek <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
@@ -38,7 +38,6 @@ msgid "Venmo"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
||||
#: pretix/static/pretixpresale/js/walletdetection.js:38
|
||||
msgid "Apple Pay"
|
||||
msgstr ""
|
||||
|
||||
@@ -141,8 +140,8 @@ msgid "Continue"
|
||||
msgstr "Συνέχεια"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:204
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:235
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:157
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:188
|
||||
msgid "Confirming your payment …"
|
||||
msgstr ""
|
||||
|
||||
@@ -164,15 +163,15 @@ msgstr "Πληρωμένες παραγγελίες"
|
||||
msgid "Total revenue"
|
||||
msgstr "Συνολικά κέρδη"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:13
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:12
|
||||
msgid "Contacting Stripe …"
|
||||
msgstr "Επικοινωνία με το Stripe …"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:70
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:65
|
||||
msgid "Total"
|
||||
msgstr "Σύνολο"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:211
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:164
|
||||
#, fuzzy
|
||||
#| msgid "Contacting Stripe …"
|
||||
msgid "Contacting your bank …"
|
||||
@@ -337,8 +336,8 @@ msgstr ""
|
||||
msgid "close"
|
||||
msgstr "Κλείσιμο"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:58
|
||||
#: pretix/static/pretixbase/js/asynctask.js:135
|
||||
#: pretix/static/pretixbase/js/asynctask.js:43
|
||||
#: pretix/static/pretixbase/js/asynctask.js:120
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "Your request has been queued on the server and will now be processed. "
|
||||
@@ -351,8 +350,8 @@ msgstr ""
|
||||
"επεξεργασία. Ανάλογα με το μέγεθος του συμβάντος σας, αυτό μπορεί να "
|
||||
"διαρκέσει μερικά λεπτά."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:63
|
||||
#: pretix/static/pretixbase/js/asynctask.js:140
|
||||
#: pretix/static/pretixbase/js/asynctask.js:48
|
||||
#: pretix/static/pretixbase/js/asynctask.js:125
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "Your request has been queued on the server and will now be processed. "
|
||||
@@ -363,8 +362,8 @@ msgstr ""
|
||||
"επεξεργασία. Ανάλογα με το μέγεθος του συμβάντος σας, αυτό μπορεί να "
|
||||
"διαρκέσει μερικά λεπτά."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:69
|
||||
#: pretix/static/pretixbase/js/asynctask.js:146
|
||||
#: pretix/static/pretixbase/js/asynctask.js:54
|
||||
#: pretix/static/pretixbase/js/asynctask.js:131
|
||||
msgid ""
|
||||
"Your request arrived on the server but we still wait for it to be processed. "
|
||||
"If this takes longer than two minutes, please contact us or go back in your "
|
||||
@@ -374,14 +373,14 @@ msgstr ""
|
||||
"του. Αν αυτό διαρκεί περισσότερο από δύο λεπτά, επικοινωνήστε μαζί μας ή "
|
||||
"επιστρέψτε στο πρόγραμμα περιήγησής σας και δοκιμάστε ξανά."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:105
|
||||
#: pretix/static/pretixbase/js/asynctask.js:193
|
||||
#: pretix/static/pretixbase/js/asynctask.js:198
|
||||
#: pretix/static/pretixbase/js/asynctask.js:90
|
||||
#: pretix/static/pretixbase/js/asynctask.js:178
|
||||
#: pretix/static/pretixbase/js/asynctask.js:183
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:24
|
||||
msgid "An error of type {code} occurred."
|
||||
msgstr "Παρουσιάστηκε σφάλμα τύπου {code}."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:108
|
||||
#: pretix/static/pretixbase/js/asynctask.js:93
|
||||
msgid ""
|
||||
"We currently cannot reach the server, but we keep trying. Last error code: "
|
||||
"{code}"
|
||||
@@ -389,14 +388,14 @@ msgstr ""
|
||||
"Αυτήν τη στιγμή δεν μπορούμε να φτάσουμε στο διακομιστή, αλλά συνεχίζουμε να "
|
||||
"προσπαθούμε. Τελευταίος κωδικός σφάλματος: {code}"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:160
|
||||
#: pretix/static/pretixbase/js/asynctask.js:145
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:21
|
||||
#, fuzzy
|
||||
#| msgid "The request took to long. Please try again."
|
||||
msgid "The request took too long. Please try again."
|
||||
msgstr "Το αίτημα διήρκησε πολύ. Παρακαλώ προσπαθήστε ξανά."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:201
|
||||
#: pretix/static/pretixbase/js/asynctask.js:186
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:26
|
||||
msgid ""
|
||||
"We currently cannot reach the server. Please try again. Error code: {code}"
|
||||
@@ -404,11 +403,11 @@ msgstr ""
|
||||
"Αυτήν τη στιγμή δεν μπορούμε να συνδεθούμε με το διακομιστή. Παρακαλώ "
|
||||
"προσπαθήστε ξανά. Κωδικός σφάλματος: {code}"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:230
|
||||
#: pretix/static/pretixbase/js/asynctask.js:215
|
||||
msgid "We are processing your request …"
|
||||
msgstr "Επεξεργαζόμαστε το αίτημά σας …"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:238
|
||||
#: pretix/static/pretixbase/js/asynctask.js:223
|
||||
msgid ""
|
||||
"We are currently sending your request to the server. If this takes longer "
|
||||
"than one minute, please check your internet connection and then reload this "
|
||||
@@ -418,7 +417,7 @@ msgstr ""
|
||||
"περισσότερο από ένα λεπτό, ελέγξτε τη σύνδεσή σας στο διαδίκτυο και στη "
|
||||
"συνέχεια επαναλάβετε τη φόρτωση αυτής της σελίδας και δοκιμάστε ξανά."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:301
|
||||
#: pretix/static/pretixbase/js/asynctask.js:285
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:71
|
||||
msgid "Close message"
|
||||
msgstr "Κλείσιμο μηνύματος"
|
||||
@@ -593,18 +592,18 @@ msgstr "Δημιουργία μηνυμάτων …"
|
||||
msgid "Unknown error."
|
||||
msgstr "Άγνωστο σφάλμα."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:311
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:309
|
||||
msgid "Your color has great contrast and is very easy to read!"
|
||||
msgstr ""
|
||||
"Το χρώμα σας έχει μεγάλη αντίθεση και είναι πολύ εύκολο να το διαβάσετε!"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:315
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:313
|
||||
msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||
msgstr ""
|
||||
"Το χρώμα σας έχει αξιοπρεπή αντίθεση και είναι ίσως αρκετά καλό για να "
|
||||
"διαβάσετε!"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:319
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:317
|
||||
msgid ""
|
||||
"Your color has bad contrast for text on white background, please choose a "
|
||||
"darker shade."
|
||||
@@ -612,32 +611,32 @@ msgstr ""
|
||||
"Το χρώμα σας έχει κακή αντίθεση για κείμενο σε λευκό φόντο, επιλέξτε μια πιο "
|
||||
"σκούρα σκιά."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:468
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:488
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:466
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||
msgid "Search query"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:484
|
||||
msgid "All"
|
||||
msgstr "Όλα"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:487
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:485
|
||||
msgid "None"
|
||||
msgstr "Κανένας"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:491
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:489
|
||||
msgid "Selected only"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:892
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Χρησιμοποιήστε διαφορετικό όνομα εσωτερικά"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:934
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:932
|
||||
msgid "Click to close"
|
||||
msgstr "Κάντε κλικ για να κλείσετε"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1009
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1007
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr ""
|
||||
|
||||
@@ -709,29 +708,25 @@ msgstr "απο %(currency)s %(price)s"
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:412
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:436
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "Εισαγάγετε μια ποσότητα για έναν από τους τύπους εισιτηρίων."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:448
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:472
|
||||
#, fuzzy
|
||||
#| msgid "Cart expired"
|
||||
msgid "required"
|
||||
msgstr "Το καλάθι έληξε"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:570
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:575
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:594
|
||||
msgid "Time zone:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:561
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:585
|
||||
msgid "Your local time:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/walletdetection.js:39
|
||||
msgid "Google Pay"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
||||
msgctxt "widget"
|
||||
msgid "Quantity"
|
||||
|
||||
+2317
-2512
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-07-21 11:46+0000\n"
|
||||
"POT-Creation-Date: 2023-06-16 14:35+0000\n"
|
||||
"PO-Revision-Date: 2021-11-25 21:00+0000\n"
|
||||
"Last-Translator: Ismael Menéndez Fernández <ismael.menendez@balidea.com>\n"
|
||||
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
@@ -38,7 +38,6 @@ msgid "Venmo"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
||||
#: pretix/static/pretixpresale/js/walletdetection.js:38
|
||||
msgid "Apple Pay"
|
||||
msgstr ""
|
||||
|
||||
@@ -138,8 +137,8 @@ msgid "Continue"
|
||||
msgstr "Continuar"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:204
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:235
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:157
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:188
|
||||
msgid "Confirming your payment …"
|
||||
msgstr "Confirmando el pago…"
|
||||
|
||||
@@ -161,15 +160,15 @@ msgstr "Órdenes pagadas"
|
||||
msgid "Total revenue"
|
||||
msgstr "Ingresos totales"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:13
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:12
|
||||
msgid "Contacting Stripe …"
|
||||
msgstr "Contactando con Stripe…"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:70
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:65
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:211
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:164
|
||||
msgid "Contacting your bank …"
|
||||
msgstr "Contactando con el banco…"
|
||||
|
||||
@@ -323,8 +322,8 @@ msgstr "Actualmente adentro"
|
||||
msgid "close"
|
||||
msgstr "Cerrar"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:58
|
||||
#: pretix/static/pretixbase/js/asynctask.js:135
|
||||
#: pretix/static/pretixbase/js/asynctask.js:43
|
||||
#: pretix/static/pretixbase/js/asynctask.js:120
|
||||
msgid ""
|
||||
"Your request is currently being processed. Depending on the size of your "
|
||||
"event, this might take up to a few minutes."
|
||||
@@ -332,13 +331,13 @@ msgstr ""
|
||||
"Su solicitud está siendo procesada. Esto puede tardar varios minutos, "
|
||||
"dependiendo del tamaño de su evento."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:63
|
||||
#: pretix/static/pretixbase/js/asynctask.js:140
|
||||
#: pretix/static/pretixbase/js/asynctask.js:48
|
||||
#: pretix/static/pretixbase/js/asynctask.js:125
|
||||
msgid "Your request has been queued on the server and will soon be processed."
|
||||
msgstr "Su solicitud ha sido enviada al servidor y será procesada en breve."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:69
|
||||
#: pretix/static/pretixbase/js/asynctask.js:146
|
||||
#: pretix/static/pretixbase/js/asynctask.js:54
|
||||
#: pretix/static/pretixbase/js/asynctask.js:131
|
||||
msgid ""
|
||||
"Your request arrived on the server but we still wait for it to be processed. "
|
||||
"If this takes longer than two minutes, please contact us or go back in your "
|
||||
@@ -348,14 +347,14 @@ msgstr ""
|
||||
"Si toma más de dos minutos, por favor contáctenos o regrese a la página "
|
||||
"anterior en su navegador e intente de nuevo."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:105
|
||||
#: pretix/static/pretixbase/js/asynctask.js:193
|
||||
#: pretix/static/pretixbase/js/asynctask.js:198
|
||||
#: pretix/static/pretixbase/js/asynctask.js:90
|
||||
#: pretix/static/pretixbase/js/asynctask.js:178
|
||||
#: pretix/static/pretixbase/js/asynctask.js:183
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:24
|
||||
msgid "An error of type {code} occurred."
|
||||
msgstr "Ha ocurrido un error de tipo {code}."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:108
|
||||
#: pretix/static/pretixbase/js/asynctask.js:93
|
||||
msgid ""
|
||||
"We currently cannot reach the server, but we keep trying. Last error code: "
|
||||
"{code}"
|
||||
@@ -363,12 +362,12 @@ msgstr ""
|
||||
"Ahora mismo no podemos contactar con el servidor, pero lo seguimos "
|
||||
"intentando. El último código de error fue: {code}"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:160
|
||||
#: pretix/static/pretixbase/js/asynctask.js:145
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:21
|
||||
msgid "The request took too long. Please try again."
|
||||
msgstr "La solicitud ha tomado demasiado tiempo. Por favor, intente de nuevo."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:201
|
||||
#: pretix/static/pretixbase/js/asynctask.js:186
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:26
|
||||
msgid ""
|
||||
"We currently cannot reach the server. Please try again. Error code: {code}"
|
||||
@@ -376,11 +375,11 @@ msgstr ""
|
||||
"Ahora mismo no podemos contactar con el servidor. Por favor, intente de "
|
||||
"nuevo. Código de error: {code}"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:230
|
||||
#: pretix/static/pretixbase/js/asynctask.js:215
|
||||
msgid "We are processing your request …"
|
||||
msgstr "Estamos procesando su solicitud…"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:238
|
||||
#: pretix/static/pretixbase/js/asynctask.js:223
|
||||
msgid ""
|
||||
"We are currently sending your request to the server. If this takes longer "
|
||||
"than one minute, please check your internet connection and then reload this "
|
||||
@@ -390,7 +389,7 @@ msgstr ""
|
||||
"minuto, por favor, revise su conexión a Internet, recargue la página e "
|
||||
"intente nuevamente."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:301
|
||||
#: pretix/static/pretixbase/js/asynctask.js:285
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:71
|
||||
msgid "Close message"
|
||||
msgstr "Cerrar mensaje"
|
||||
@@ -560,17 +559,17 @@ msgstr "Generando mensajes…"
|
||||
msgid "Unknown error."
|
||||
msgstr "Error desconocido."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:311
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:309
|
||||
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!"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:315
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:313
|
||||
msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||
msgstr ""
|
||||
"¡Tu color tiene un contraste decente y es probablemente lo suficientemente "
|
||||
"legible!"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:319
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:317
|
||||
msgid ""
|
||||
"Your color has bad contrast for text on white background, please choose a "
|
||||
"darker shade."
|
||||
@@ -578,32 +577,32 @@ msgstr ""
|
||||
"Tu color tiene mal contraste para un texto con fondo blanco, por favor, "
|
||||
"escoge un tono más oscuro."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:468
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:488
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:466
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||
msgid "Search query"
|
||||
msgstr "Consultar búsqueda"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:484
|
||||
msgid "All"
|
||||
msgstr "Todos"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:487
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:485
|
||||
msgid "None"
|
||||
msgstr "Ninguno"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:491
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:489
|
||||
msgid "Selected only"
|
||||
msgstr "Solamente seleccionados"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:892
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Usar un nombre diferente internamente"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:934
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:932
|
||||
msgid "Click to close"
|
||||
msgstr "Click para cerrar"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1009
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1007
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "¡Tienes cambios sin guardar!"
|
||||
|
||||
@@ -667,27 +666,23 @@ msgstr "Obtienes %(currency)s %(price)s de vuelta"
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr "Por favor, ingrese el monto que el organizador puede quedarse."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:412
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:436
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "Por favor, introduzca un valor para cada tipo de entrada."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:448
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:472
|
||||
msgid "required"
|
||||
msgstr "campo requerido"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:570
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:575
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:594
|
||||
msgid "Time zone:"
|
||||
msgstr "Zona horaria:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:561
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:585
|
||||
msgid "Your local time:"
|
||||
msgstr "Su hora local:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/walletdetection.js:39
|
||||
msgid "Google Pay"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
||||
msgctxt "widget"
|
||||
msgid "Quantity"
|
||||
|
||||
+1913
-2247
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-07-21 11:46+0000\n"
|
||||
"POT-Creation-Date: 2023-06-16 14:35+0000\n"
|
||||
"PO-Revision-Date: 2021-11-10 05:00+0000\n"
|
||||
"Last-Translator: Jaakko Rinta-Filppula <jaakko@r-f.fi>\n"
|
||||
"Language-Team: Finnish <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
@@ -38,7 +38,6 @@ msgid "Venmo"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
||||
#: pretix/static/pretixpresale/js/walletdetection.js:38
|
||||
msgid "Apple Pay"
|
||||
msgstr ""
|
||||
|
||||
@@ -141,8 +140,8 @@ msgid "Continue"
|
||||
msgstr "Jatka"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:204
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:235
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:157
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:188
|
||||
msgid "Confirming your payment …"
|
||||
msgstr "Maksuasi vahvistetaan …"
|
||||
|
||||
@@ -164,15 +163,15 @@ msgstr "Maksetut tilaukset"
|
||||
msgid "Total revenue"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:13
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:12
|
||||
msgid "Contacting Stripe …"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:70
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:65
|
||||
msgid "Total"
|
||||
msgstr "Summa"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:211
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:164
|
||||
msgid "Contacting your bank …"
|
||||
msgstr ""
|
||||
|
||||
@@ -335,45 +334,45 @@ msgstr ""
|
||||
msgid "close"
|
||||
msgstr "Sulje"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:58
|
||||
#: pretix/static/pretixbase/js/asynctask.js:135
|
||||
#: pretix/static/pretixbase/js/asynctask.js:43
|
||||
#: pretix/static/pretixbase/js/asynctask.js:120
|
||||
msgid ""
|
||||
"Your request is currently being processed. Depending on the size of your "
|
||||
"event, this might take up to a few minutes."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:63
|
||||
#: pretix/static/pretixbase/js/asynctask.js:140
|
||||
#: pretix/static/pretixbase/js/asynctask.js:48
|
||||
#: pretix/static/pretixbase/js/asynctask.js:125
|
||||
msgid "Your request has been queued on the server and will soon be processed."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:69
|
||||
#: pretix/static/pretixbase/js/asynctask.js:146
|
||||
#: pretix/static/pretixbase/js/asynctask.js:54
|
||||
#: pretix/static/pretixbase/js/asynctask.js:131
|
||||
msgid ""
|
||||
"Your request arrived on the server but we still wait for it to be processed. "
|
||||
"If this takes longer than two minutes, please contact us or go back in your "
|
||||
"browser and try again."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:105
|
||||
#: pretix/static/pretixbase/js/asynctask.js:193
|
||||
#: pretix/static/pretixbase/js/asynctask.js:198
|
||||
#: pretix/static/pretixbase/js/asynctask.js:90
|
||||
#: pretix/static/pretixbase/js/asynctask.js:178
|
||||
#: pretix/static/pretixbase/js/asynctask.js:183
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:24
|
||||
msgid "An error of type {code} occurred."
|
||||
msgstr "Tapahtui virhe. Virhekoodi: {code}."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:108
|
||||
#: pretix/static/pretixbase/js/asynctask.js:93
|
||||
msgid ""
|
||||
"We currently cannot reach the server, but we keep trying. Last error code: "
|
||||
"{code}"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:160
|
||||
#: pretix/static/pretixbase/js/asynctask.js:145
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:21
|
||||
msgid "The request took too long. Please try again."
|
||||
msgstr "Pyyntö aikakatkaistiin. Ole hyvä ja yritä uudelleen."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:201
|
||||
#: pretix/static/pretixbase/js/asynctask.js:186
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:26
|
||||
msgid ""
|
||||
"We currently cannot reach the server. Please try again. Error code: {code}"
|
||||
@@ -381,18 +380,18 @@ msgstr ""
|
||||
"Palvelimeen ei juuri nyt saatu yhteyttä. Ole hyvä ja yritä uudelleen. "
|
||||
"Virhekoodi: {code}"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:230
|
||||
#: pretix/static/pretixbase/js/asynctask.js:215
|
||||
msgid "We are processing your request …"
|
||||
msgstr "Pyyntöäsi käsitellään …"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:238
|
||||
#: pretix/static/pretixbase/js/asynctask.js:223
|
||||
msgid ""
|
||||
"We are currently sending your request to the server. If this takes longer "
|
||||
"than one minute, please check your internet connection and then reload this "
|
||||
"page and try again."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:301
|
||||
#: pretix/static/pretixbase/js/asynctask.js:285
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:71
|
||||
msgid "Close message"
|
||||
msgstr "Sulje viesti"
|
||||
@@ -563,46 +562,46 @@ msgstr ""
|
||||
msgid "Unknown error."
|
||||
msgstr "Tuntematon virhe."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:311
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:309
|
||||
msgid "Your color has great contrast and is very easy to read!"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:315
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:313
|
||||
msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:319
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:317
|
||||
msgid ""
|
||||
"Your color has bad contrast for text on white background, please choose a "
|
||||
"darker shade."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:468
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:488
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:466
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||
msgid "Search query"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:484
|
||||
msgid "All"
|
||||
msgstr "Kaikki"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:487
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:485
|
||||
msgid "None"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:491
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:489
|
||||
msgid "Selected only"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:892
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Käytä toista nimeä sisäisesti"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:934
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:932
|
||||
msgid "Click to close"
|
||||
msgstr "Sulje klikkaamalla"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1009
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1007
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Sinulla on tallentamattomia muutoksia!"
|
||||
|
||||
@@ -666,29 +665,25 @@ msgstr ""
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:412
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:436
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:448
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:472
|
||||
#, fuzzy
|
||||
#| msgid "Cart expired"
|
||||
msgid "required"
|
||||
msgstr "Ostoskori on vanhentunut"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:570
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:575
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:594
|
||||
msgid "Time zone:"
|
||||
msgstr "Aikavyöhyke:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:561
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:585
|
||||
msgid "Your local time:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/walletdetection.js:39
|
||||
msgid "Google Pay"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
||||
msgctxt "widget"
|
||||
msgid "Quantity"
|
||||
|
||||
+3030
-3454
File diff suppressed because it is too large
Load Diff
@@ -6,8 +6,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: French\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-07-21 11:46+0000\n"
|
||||
"PO-Revision-Date: 2023-07-19 17:00+0000\n"
|
||||
"POT-Creation-Date: 2023-06-16 14:35+0000\n"
|
||||
"PO-Revision-Date: 2023-06-17 09:09+0000\n"
|
||||
"Last-Translator: Ronan LE MEILLAT <ronan.le_meillat@highcanfly.club>\n"
|
||||
"Language-Team: French <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
"fr/>\n"
|
||||
@@ -37,7 +37,6 @@ msgid "Venmo"
|
||||
msgstr "Venmo"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
||||
#: pretix/static/pretixpresale/js/walletdetection.js:38
|
||||
msgid "Apple Pay"
|
||||
msgstr "Apple Pay"
|
||||
|
||||
@@ -135,8 +134,8 @@ msgid "Continue"
|
||||
msgstr "Continuer"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:222
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:204
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:235
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:157
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:188
|
||||
msgid "Confirming your payment …"
|
||||
msgstr "Confirmation de votre paiement…"
|
||||
|
||||
@@ -158,15 +157,15 @@ msgstr "Commandes payées"
|
||||
msgid "Total revenue"
|
||||
msgstr "Total des revenus"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:13
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:12
|
||||
msgid "Contacting Stripe …"
|
||||
msgstr "Communication avec Stripe …"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:70
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:65
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:211
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:164
|
||||
msgid "Contacting your bank …"
|
||||
msgstr "Communication avec votre banque …"
|
||||
|
||||
@@ -282,7 +281,7 @@ msgstr "Entrée non autorisée"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:60
|
||||
msgid "Ticket code revoked/changed"
|
||||
msgstr "Code du billet révoqué/modifié"
|
||||
msgstr "Code du billet révoqué/changé"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:61
|
||||
msgid "Ticket blocked"
|
||||
@@ -316,8 +315,8 @@ msgstr "Présent actuellement"
|
||||
msgid "close"
|
||||
msgstr "fermer"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:58
|
||||
#: pretix/static/pretixbase/js/asynctask.js:135
|
||||
#: pretix/static/pretixbase/js/asynctask.js:43
|
||||
#: pretix/static/pretixbase/js/asynctask.js:120
|
||||
msgid ""
|
||||
"Your request is currently being processed. Depending on the size of your "
|
||||
"event, this might take up to a few minutes."
|
||||
@@ -325,15 +324,15 @@ msgstr ""
|
||||
"Votre demande est maintenant en cours de traitement. Selon la taille de "
|
||||
"votre événement, cela peut prendre jusqu' à quelques minutes."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:63
|
||||
#: pretix/static/pretixbase/js/asynctask.js:140
|
||||
#: pretix/static/pretixbase/js/asynctask.js:48
|
||||
#: pretix/static/pretixbase/js/asynctask.js:125
|
||||
msgid "Your request has been queued on the server and will soon be processed."
|
||||
msgstr ""
|
||||
"Votre demande a été mise en attente sur le serveur et sera traitée "
|
||||
"prochainement."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:69
|
||||
#: pretix/static/pretixbase/js/asynctask.js:146
|
||||
#: pretix/static/pretixbase/js/asynctask.js:54
|
||||
#: pretix/static/pretixbase/js/asynctask.js:131
|
||||
msgid ""
|
||||
"Your request arrived on the server but we still wait for it to be processed. "
|
||||
"If this takes longer than two minutes, please contact us or go back in your "
|
||||
@@ -343,14 +342,14 @@ msgstr ""
|
||||
"prend plus de deux minutes, veuillez nous contacter ou retourner dans votre "
|
||||
"navigateur et réessayer."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:105
|
||||
#: pretix/static/pretixbase/js/asynctask.js:193
|
||||
#: pretix/static/pretixbase/js/asynctask.js:198
|
||||
#: pretix/static/pretixbase/js/asynctask.js:90
|
||||
#: pretix/static/pretixbase/js/asynctask.js:178
|
||||
#: pretix/static/pretixbase/js/asynctask.js:183
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:24
|
||||
msgid "An error of type {code} occurred."
|
||||
msgstr "Une erreur de type {code} s'est produite."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:108
|
||||
#: pretix/static/pretixbase/js/asynctask.js:93
|
||||
msgid ""
|
||||
"We currently cannot reach the server, but we keep trying. Last error code: "
|
||||
"{code}"
|
||||
@@ -358,12 +357,12 @@ msgstr ""
|
||||
"Nous ne pouvons actuellement pas atteindre le serveur, mais nous continuons "
|
||||
"d'essayer. Dernier code d'erreur : {code}"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:160
|
||||
#: pretix/static/pretixbase/js/asynctask.js:145
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:21
|
||||
msgid "The request took too long. Please try again."
|
||||
msgstr "La requête a prit trop de temps. Veuillez réessayer."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:201
|
||||
#: pretix/static/pretixbase/js/asynctask.js:186
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:26
|
||||
msgid ""
|
||||
"We currently cannot reach the server. Please try again. Error code: {code}"
|
||||
@@ -371,11 +370,11 @@ msgstr ""
|
||||
"Actuellement, nous ne pouvons pas atteindre le serveur. Veuillez réessayer. "
|
||||
"Code d'erreur : {code}"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:230
|
||||
#: pretix/static/pretixbase/js/asynctask.js:215
|
||||
msgid "We are processing your request …"
|
||||
msgstr "Nous traitons votre demande …"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:238
|
||||
#: pretix/static/pretixbase/js/asynctask.js:223
|
||||
msgid ""
|
||||
"We are currently sending your request to the server. If this takes longer "
|
||||
"than one minute, please check your internet connection and then reload this "
|
||||
@@ -385,7 +384,7 @@ msgstr ""
|
||||
"d'une minute, veuillez vérifier votre connexion Internet, puis recharger "
|
||||
"cette page et réessayer."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:301
|
||||
#: pretix/static/pretixbase/js/asynctask.js:285
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:71
|
||||
msgid "Close message"
|
||||
msgstr "Fermer le message"
|
||||
@@ -556,16 +555,16 @@ msgstr "Création de messages …"
|
||||
msgid "Unknown error."
|
||||
msgstr "Erreur inconnue."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:311
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:309
|
||||
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 !"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:315
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:313
|
||||
msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||
msgstr ""
|
||||
"Votre choix de couleur est assez bon pour la lecture et a un bon contraste !"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:319
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:317
|
||||
msgid ""
|
||||
"Your color has bad contrast for text on white background, please choose a "
|
||||
"darker shade."
|
||||
@@ -573,32 +572,32 @@ msgstr ""
|
||||
"Votre choix de couleur n'a pas un bon contraste avec du texte sur un fond "
|
||||
"blanc, SVP choisissez un ton plus sombre."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:468
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:488
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:466
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||
msgid "Search query"
|
||||
msgstr "Requête de recherche"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:486
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:484
|
||||
msgid "All"
|
||||
msgstr "Tous"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:487
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:485
|
||||
msgid "None"
|
||||
msgstr "Aucun"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:491
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:489
|
||||
msgid "Selected only"
|
||||
msgstr "Seuls les sélectionnés"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:892
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Utiliser un nom différent en interne"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:934
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:932
|
||||
msgid "Click to close"
|
||||
msgstr "Cliquez pour fermer"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1009
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:1007
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Vous avez des modifications non sauvegardées !"
|
||||
|
||||
@@ -658,32 +657,25 @@ msgstr "Vous recevez en retour %(currency)s %(amount)s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:195
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr ""
|
||||
"Veuillez indiquer le montant que l'organisateur est autorisé à retenir."
|
||||
msgstr "Veuillez indiquer le montant que l'organisateur est autorisé à retenir."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:412
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:436
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "SVP entrez une quantité pour un de vos types de billets."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:448
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:472
|
||||
msgid "required"
|
||||
msgstr "obligatoire"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:570
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:575
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:594
|
||||
msgid "Time zone:"
|
||||
msgstr "Fuseau horaire :"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:561
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:585
|
||||
msgid "Your local time:"
|
||||
msgstr "Votre heure locale :"
|
||||
|
||||
#: 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
|
||||
msgctxt "widget"
|
||||
msgid "Quantity"
|
||||
|
||||
+1924
-2269
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user