mirror of
https://github.com/pretix/pretix.git
synced 2026-08-11 10:57:00 +00:00
Compare commits
94
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3badcfcccb | ||
|
|
a61609a89d | ||
|
|
8ec1b83d25 | ||
|
|
943b319557 | ||
|
|
28b13667ce | ||
|
|
b00d1c9156 | ||
|
|
120317a8f2 | ||
|
|
7d5b00a610 | ||
|
|
83612c7d65 | ||
|
|
7a5f96369a | ||
|
|
7fd6bf41f9 | ||
|
|
458c3d4b83 | ||
|
|
d10d061e45 | ||
|
|
d30bca50f7 | ||
|
|
3903aca7c9 | ||
|
|
493c920aba | ||
|
|
09b7bc00b0 | ||
|
|
2e195c0274 | ||
|
|
18cb9c1816 | ||
|
|
c3e0120f9f | ||
|
|
67f7fec134 | ||
|
|
c2c97f31ca | ||
|
|
fd565ecdb2 | ||
|
|
f35b13b686 | ||
|
|
550bb675f5 | ||
|
|
adc9c9d514 | ||
|
|
8441c4bc7a | ||
|
|
97ff252c09 | ||
|
|
d3ca2ac1e5 | ||
|
|
6eebaaa563 | ||
|
|
c9781f012b | ||
|
|
000bf54105 | ||
|
|
e42d3d632f | ||
|
|
3bf5a5e478 | ||
|
|
a6f31df0d4 | ||
|
|
d40492748a | ||
|
|
f4ca230af7 | ||
|
|
4fb1748bf6 | ||
|
|
9d668af102 | ||
|
|
80fd4a3b2a | ||
|
|
65bb2283d4 | ||
|
|
00751e8911 | ||
|
|
957a066475 | ||
|
|
7e077bdd7e | ||
|
|
e80d84ec3c | ||
|
|
dc3b742d8c | ||
|
|
1f5fe1b237 | ||
|
|
a8997f8971 | ||
|
|
eb068f524c | ||
|
|
f615595547 | ||
|
|
8bd78eefcf | ||
|
|
848f7fa0e5 | ||
|
|
3442a543c8 | ||
|
|
f88c24863d | ||
|
|
79c5160b57 | ||
|
|
e8492cad3c | ||
|
|
7ea5a2b59e | ||
|
|
4c373518d0 | ||
|
|
1521c0cfcd | ||
|
|
3432e62e4f | ||
|
|
736fa38ca7 | ||
|
|
d14dc4c5ff | ||
|
|
5db1a5b8af | ||
|
|
86a51afe9e | ||
|
|
ea9c85a1b4 | ||
|
|
226ff9b044 | ||
|
|
d8991d8138 | ||
|
|
83642ec9f3 | ||
|
|
9f0ce28ce4 | ||
|
|
28722fecbd | ||
|
|
10a5d4ac68 | ||
|
|
ba36f6d2ce | ||
|
|
1e301da26c | ||
|
|
d0307b9936 | ||
|
|
c083ce904a | ||
|
|
694b915d89 | ||
|
|
ea928ea7d4 | ||
|
|
36d49fbd77 | ||
|
|
f0cb451c34 | ||
|
|
2c7fcd0599 | ||
|
|
034722fa39 | ||
|
|
5a6870fce1 | ||
|
|
de28425993 | ||
|
|
f3eb0d2dba | ||
|
|
0630e05d50 | ||
|
|
f868507670 | ||
|
|
04032078c1 | ||
|
|
8af2714a04 | ||
|
|
c4b9cc4143 | ||
|
|
8c132d8342 | ||
|
|
8e63fafc62 | ||
|
|
63ebe16fd3 | ||
|
|
775fdd1ccb | ||
|
|
784577d86f |
+1
-2
@@ -10,8 +10,7 @@ tests:
|
|||||||
- cd src
|
- cd src
|
||||||
- python manage.py check
|
- python manage.py check
|
||||||
- make all compress
|
- make all compress
|
||||||
- playwright install
|
- PRETIX_CONFIG_FILE=tests/ci_sqlite.cfg py.test -n 3 tests --ignore=tests/e2e --maxfail=100
|
||||||
- PRETIX_CONFIG_FILE=tests/ci_sqlite.cfg py.test -n 3 tests --maxfail=100
|
|
||||||
except:
|
except:
|
||||||
- '/^v.*$/'
|
- '/^v.*$/'
|
||||||
pypi:
|
pypi:
|
||||||
|
|||||||
+1
-2
@@ -57,8 +57,7 @@ COPY vite.config.ts /pretix/vite.config.ts
|
|||||||
|
|
||||||
RUN pip3 install -U \
|
RUN pip3 install -U \
|
||||||
pip \
|
pip \
|
||||||
setuptools \
|
setuptools && \
|
||||||
wheel && \
|
|
||||||
cd /pretix && \
|
cd /pretix && \
|
||||||
PRETIX_DOCKER_BUILD=TRUE pip3 install \
|
PRETIX_DOCKER_BUILD=TRUE pip3 install \
|
||||||
-e ".[memcached]" \
|
-e ".[memcached]" \
|
||||||
|
|||||||
@@ -46,12 +46,14 @@ Checking a ticket in
|
|||||||
this request twice with the same nonce, the second request will also succeed but will always
|
this request twice with the same nonce, the second request will also succeed but will always
|
||||||
create only one check-in object even when the previous request was successful as well. This
|
create only one check-in object even when the previous request was successful as well. This
|
||||||
allows for a certain level of idempotency and enables you to re-try after a connection failure.
|
allows for a certain level of idempotency and enables you to re-try after a connection failure.
|
||||||
|
:<json string exchange_medium_type: To perform an exchange to a reusable medium, pass the type of the new reusable medium
|
||||||
|
:<json string exchange_medium_identifier: To perform an exchange to a reusable media, pass the identifier of the new medium
|
||||||
:<json boolean use_order_locale: Specifies that pretix should use the customer's language (``locale`` field from the
|
:<json boolean use_order_locale: Specifies that pretix should use the customer's language (``locale`` field from the
|
||||||
order) when building texts (currently only the ``reason_explanation`` response field).
|
order) when building texts (currently only the ``reason_explanation`` response field).
|
||||||
Defaults to ``false`` in which case the server will determine the language (currently
|
Defaults to ``false`` in which case the server will determine the language (currently
|
||||||
the event default language, might change in the future with support for the
|
the event default language, might change in the future with support for the
|
||||||
``Accept-Language`` header).
|
``Accept-Language`` header).
|
||||||
:>json string status: ``"ok"``, ``"incomplete"``, or ``"error"``
|
:>json string status: ``"ok"``, ``"incomplete"``, ``"exchange"``, or ``"error"``
|
||||||
:>json string reason: Reason code, only set on status ``"error"``, see below for possible values.
|
:>json string reason: Reason code, only set on status ``"error"``, see below for possible values.
|
||||||
:>json string reason_explanation: Human-readable explanation, only set on status ``"error"`` and reason ``"rules"``, can be null.
|
:>json string reason_explanation: Human-readable explanation, only set on status ``"error"`` and reason ``"rules"``, can be null.
|
||||||
:>json object position: Copy of the matching order position (if any was found). The contents are the same as the
|
:>json object position: Copy of the matching order position (if any was found). The contents are the same as the
|
||||||
@@ -67,6 +69,8 @@ Checking a ticket in
|
|||||||
:>json object list: Excerpt of information about the matching :ref:`check-in list <rest-checkinlists>` (if any was found),
|
:>json object list: Excerpt of information about the matching :ref:`check-in list <rest-checkinlists>` (if any was found),
|
||||||
including the attributes ``id``, ``name``, ``event``, ``subevent``, and ``include_pending``.
|
including the attributes ``id``, ``name``, ``event``, ``subevent``, and ``include_pending``.
|
||||||
:>json object questions: List of questions to be answered for check-in, only set on status ``"incomplete"``.
|
:>json object questions: List of questions to be answered for check-in, only set on status ``"incomplete"``.
|
||||||
|
:>json object media_policy: Reusable media policy (see documentation on items), only set on status ``"exchange"``.
|
||||||
|
:>json object media_type: Reusable media type (see documentation on items), only set on status ``"exchange"``.
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
@@ -224,6 +228,9 @@ Checking a ticket in
|
|||||||
* ``ambiguous`` - Multiple tickets match scan, rejected.
|
* ``ambiguous`` - Multiple tickets match scan, rejected.
|
||||||
* ``revoked`` - Ticket code has been revoked.
|
* ``revoked`` - Ticket code has been revoked.
|
||||||
* ``unapproved`` - Order has not yet been approved.
|
* ``unapproved`` - Order has not yet been approved.
|
||||||
|
* ``already_exchanged`` - Ticket already has been exchanged for a reusable medium that must now be used for check-in.
|
||||||
|
* ``medium_invalid`` - Reusable medium identifier given was not found or is not valid.
|
||||||
|
* ``medium_exists`` - Reusable medium identifier already exists, but expected to be new.
|
||||||
* ``error`` - Internal error.
|
* ``error`` - Internal error.
|
||||||
|
|
||||||
In case of reason ``rules`` and ``invalid_time``, there might be an additional response field ``reason_explanation``
|
In case of reason ``rules`` and ``invalid_time``, there might be an additional response field ``reason_explanation``
|
||||||
|
|||||||
@@ -602,7 +602,8 @@ Order position endpoints
|
|||||||
|
|
||||||
We no longer recommend using this API if you're building a ticket scanning application, as it has a few design
|
We no longer recommend using this API if you're building a ticket scanning application, as it has a few design
|
||||||
flaws that can lead to `security issues`_ or compatibility issues due to barcode content characters that are not
|
flaws that can lead to `security issues`_ or compatibility issues due to barcode content characters that are not
|
||||||
URL-safe. We recommend to use our new :ref:`check-in API <rest-checkin>` instead.
|
URL-safe. We recommend to use our new :ref:`check-in API <rest-checkin>` instead. Advanced features like medium
|
||||||
|
exchange are only supported on the new API.
|
||||||
|
|
||||||
:query boolean untrusted_input: If set to true, the lookup parameter is **always** interpreted as a ``secret``, never
|
:query boolean untrusted_input: If set to true, the lookup parameter is **always** interpreted as a ``secret``, never
|
||||||
as an ``id``. This should be always set if you are passing through untrusted, scanned
|
as an ``id``. This should be always set if you are passing through untrusted, scanned
|
||||||
@@ -741,6 +742,9 @@ Order position endpoints
|
|||||||
* ``ambiguous`` - Multiple tickets match scan, rejected.
|
* ``ambiguous`` - Multiple tickets match scan, rejected.
|
||||||
* ``revoked`` - Ticket code has been revoked.
|
* ``revoked`` - Ticket code has been revoked.
|
||||||
* ``unapproved`` - Order has not yet been approved.
|
* ``unapproved`` - Order has not yet been approved.
|
||||||
|
* ``already_exchanged`` - Ticket already has been exchanged for a reusable medium that must now be used for check-in.
|
||||||
|
* ``medium_invalid`` - Reusable medium identifier given was not found and could not be automatically created.
|
||||||
|
* ``medium_exists`` - Reusable medium identifier already exists, but expected to be new.
|
||||||
|
|
||||||
In case of reason ``rules`` or ``invalid_time``, there might be an additional response field ``reason_explanation``
|
In case of reason ``rules`` or ``invalid_time``, there might be an additional response field ``reason_explanation``
|
||||||
with a human-readable description of the violated rules. However, that field can also be missing or be ``null``.
|
with a human-readable description of the violated rules. However, that field can also be missing or be ``null``.
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ allow_waitinglist boolean If ``false``,
|
|||||||
product when it is sold out.
|
product when it is sold out.
|
||||||
issue_giftcard boolean If ``true``, buying this product will yield a gift card.
|
issue_giftcard boolean If ``true``, buying this product will yield a gift card.
|
||||||
media_policy string Policy on how to handle reusable media (experimental feature).
|
media_policy string Policy on how to handle reusable media (experimental feature).
|
||||||
Possible values are ``null``, ``"new"``, ``"reuse"``, and ``"reuse_or_new"``.
|
Possible values are ``null``, ``"new"``, ``"reuse"``, ``"reuse_or_new"``, ``"append"``, and ``"append_or_new"``.
|
||||||
media_type string Type of reusable media to work on (experimental feature). See :ref:`rest-reusablemedia` for possible choices.
|
media_type string Type of reusable media to work on (experimental feature). See :ref:`rest-reusablemedia` for possible choices.
|
||||||
show_quota_left boolean Publicly show how many tickets are still available.
|
show_quota_left boolean Publicly show how many tickets are still available.
|
||||||
If this is ``null``, the event default is used.
|
If this is ``null``, the event default is used.
|
||||||
|
|||||||
@@ -1069,8 +1069,7 @@ Creating orders
|
|||||||
* ``valid_from`` (optional, if both ``valid_from`` and ``valid_until`` are **missing** (not ``null``) the availability will be computed from the given product)
|
* ``valid_from`` (optional, if both ``valid_from`` and ``valid_until`` are **missing** (not ``null``) the availability will be computed from the given product)
|
||||||
* ``valid_until`` (optional, if both ``valid_from`` and ``valid_until`` are **missing** (not ``null``) the availability will be computed from the given product)
|
* ``valid_until`` (optional, if both ``valid_from`` and ``valid_until`` are **missing** (not ``null``) the availability will be computed from the given product)
|
||||||
* ``requested_valid_from`` (optional, can be set **instead** of ``valid_from`` and ``valid_until`` to signal a user choice for the start time that may or may not be respected)
|
* ``requested_valid_from`` (optional, can be set **instead** of ``valid_from`` and ``valid_until`` to signal a user choice for the start time that may or may not be respected)
|
||||||
* ``use_reusable_medium`` (optional, causes the new ticket to take over the given reusable medium, identified by its ID)
|
* ``use_reusable_medium`` (optional, causes the new ticket to be connected to the given reusable medium, identified by its ID)
|
||||||
* ``add_to_reusable_medium`` (optional, causes the new ticket to be added to the given reusable medium, identified by its ID)
|
|
||||||
* ``discount`` (optional, only possible if ``price`` is set; attention: if this is set to not-``null`` on any position, automatic calculation of discounts will not run)
|
* ``discount`` (optional, only possible if ``price`` is set; attention: if this is set to not-``null`` on any position, automatic calculation of discounts will not run)
|
||||||
* ``answers``
|
* ``answers``
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ is a python method that emulates a behavior similar to ``reverse``:
|
|||||||
|
|
||||||
If you need to communicate the URL externally, you can use a different method to ensure that it is always an absolute URL:
|
If you need to communicate the URL externally, you can use a different method to ensure that it is always an absolute URL:
|
||||||
|
|
||||||
.. autofunction:: pretix.multidomain.urlreverse.build_absolute_uri
|
.. autofunction:: pretix.multidomain.urlreverse.eventreverse_absolute
|
||||||
|
|
||||||
In addition, there is a template tag that works similar to ``url`` but takes an event or organizer object
|
In addition, there is a template tag that works similar to ``url`` but takes an event or organizer object
|
||||||
as its first argument and can be used like this::
|
as its first argument and can be used like this::
|
||||||
|
|||||||
Generated
+109
-91
@@ -370,14 +370,14 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@napi-rs/wasm-runtime": {
|
"node_modules/@napi-rs/wasm-runtime": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz",
|
||||||
"integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==",
|
"integrity": "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tybys/wasm-util": "^0.10.1"
|
"@tybys/wasm-util": "^0.10.2"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@@ -427,9 +427,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@oxc-project/types": {
|
"node_modules/@oxc-project/types": {
|
||||||
"version": "0.129.0",
|
"version": "0.133.0",
|
||||||
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.129.0.tgz",
|
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz",
|
||||||
"integrity": "sha512-3oz8m3FGdr2nDXVqmFUw7jolKliC4MoyXYIG2c7gpjBnzUWQpUGIYcXYKxTdTi+N2jusvt610ckTMkxdwHkYEg==",
|
"integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
@@ -758,9 +758,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-android-arm64": {
|
"node_modules/@rolldown/binding-android-arm64": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz",
|
||||||
"integrity": "sha512-TWMZnRLMe63C2Lhyicviu7ZHaU4kxa6PS3rofvc9GmcvptzNN11BcfQ4Sl7MwTOsisQoa2keB/EBdNCAnUo8vA==",
|
"integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -775,9 +775,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-darwin-arm64": {
|
"node_modules/@rolldown/binding-darwin-arm64": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz",
|
||||||
"integrity": "sha512-6XcD+8k0gPVItNagEw78/qqcBDwKcwDYS8V2hRmVsfUSIrd8cWe/CBvRDI5toqFyPfj+FJr6t8U6Xj2P2prEew==",
|
"integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -792,9 +792,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-darwin-x64": {
|
"node_modules/@rolldown/binding-darwin-x64": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz",
|
||||||
"integrity": "sha512-iN/tWVXRQDWvmZlKdceP1Dwug9GDpEymhb9p4xnEe6zvCg5lFmzVljl+1qR1NVx3yfGpr2Na+CuLmv5IU8uzfQ==",
|
"integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -809,9 +809,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-freebsd-x64": {
|
"node_modules/@rolldown/binding-freebsd-x64": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz",
|
||||||
"integrity": "sha512-jjQMDvvwSOuhOwMszD/klSOjyWMM3zI64hWTj9KT5x4MxRbZAf+7vLQ6qouRhtsLVFHr3f0ILaJAfgENPiQdAQ==",
|
"integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -826,9 +826,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
|
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz",
|
||||||
"integrity": "sha512-d//Dtg2x6/m3mbV64yUGNnDGNZaDGRpDLLNGerHQUVObuNaIQaaDp25yUiqGXtHEXX+NP2d0wAlmKgpYgIAJ2A==",
|
"integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -843,13 +843,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-arm64-gnu": {
|
"node_modules/@rolldown/binding-linux-arm64-gnu": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz",
|
||||||
"integrity": "sha512-n7Ofp0mx+aB2cC+Sdy5YtMnXtY9lchnHbY+3Yt0uq9JsWQExf4f5Whu0tK0R8Jdc9S6RchTHjIFY7uc92puOVQ==",
|
"integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"libc": [
|
||||||
|
"glibc"
|
||||||
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -860,13 +863,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-arm64-musl": {
|
"node_modules/@rolldown/binding-linux-arm64-musl": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz",
|
||||||
"integrity": "sha512-EIVjy2cgd7uuMMo94FVkBp7F6DhcZAUwNURkSG3RwUmvAXR6s0ISxM81U+IydcZByPG0pZIHsf1b6kTxoFDgJA==",
|
"integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"libc": [
|
||||||
|
"musl"
|
||||||
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -877,13 +883,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
|
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz",
|
||||||
"integrity": "sha512-JEwwOPcwTLAcpDQlqSmjEmfs63xJnSiUNIGvLcDLUHCWK4XowpS/7c7tUsUH6uT/ct6bMUTdXKfI8967FYj6mg==",
|
"integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"libc": [
|
||||||
|
"glibc"
|
||||||
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -894,13 +903,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-s390x-gnu": {
|
"node_modules/@rolldown/binding-linux-s390x-gnu": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz",
|
||||||
"integrity": "sha512-0wjCFhLrihtAubnT9iA0N++0pSV0z5Hg7tNGdNJ4RFaINceHadoF+kiFGyY1qSSNVIAZtLotG8Ju1bgDPkjnFA==",
|
"integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"libc": [
|
||||||
|
"glibc"
|
||||||
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -911,13 +923,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-x64-gnu": {
|
"node_modules/@rolldown/binding-linux-x64-gnu": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz",
|
||||||
"integrity": "sha512-Dfn7iak9BcMMePxcoJfpSbWqnEyrp/dRF63/8qW/eHBdOZov6x5aShLLEYGYdIeSJ6vMLK/XCVB+lGIxm41bQA==",
|
"integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"libc": [
|
||||||
|
"glibc"
|
||||||
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -928,13 +943,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-x64-musl": {
|
"node_modules/@rolldown/binding-linux-x64-musl": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz",
|
||||||
"integrity": "sha512-5/utzzDmD/pD/bmuaUcbTf/sZYy0aztwIVlfpoW1fTjCZ0BaPOMVWGZL1zvgxyi7ZIVYWlxKONHmSbHuiOh8Jw==",
|
"integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"libc": [
|
||||||
|
"musl"
|
||||||
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -945,9 +963,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-openharmony-arm64": {
|
"node_modules/@rolldown/binding-openharmony-arm64": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz",
|
||||||
"integrity": "sha512-ouJs8VcUomfLfpbUECqFMRqdV4x6aeAK3MA4m6vTrJJjKyWTV5KnxZx7Jd9G+GlDaQQxubcba00x16OyJ1meig==",
|
"integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -962,9 +980,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-wasm32-wasi": {
|
"node_modules/@rolldown/binding-wasm32-wasi": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz",
|
||||||
"integrity": "sha512-E+oHKGiDA+lsKMmFtffDDw91EryDT7uJocrIuCHqhm6bCTM6xFK+3gaCkYOHfPwQr0cCNarSM2xaELoQDz9jJg==",
|
"integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"wasm32"
|
"wasm32"
|
||||||
],
|
],
|
||||||
@@ -981,9 +999,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-win32-arm64-msvc": {
|
"node_modules/@rolldown/binding-win32-arm64-msvc": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz",
|
||||||
"integrity": "sha512-yYK02n8Rngo+gbm1y6G0+7jk1sJ/2Wt7K0me0Y7k/ErBpyf+LJ2gFpqWVTcRV1rUepBlQRmpgWkTQCiiwrK0Ow==",
|
"integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -998,9 +1016,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-win32-x64-msvc": {
|
"node_modules/@rolldown/binding-win32-x64-msvc": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz",
|
||||||
"integrity": "sha512-14bpChMahXRRXiTwahSl+zzHPW6qQTXtkMuJBFlbo+pqSAews2d4BdCSHfrJ/MBsCZtpmTafsY+1QhBzitcmdg==",
|
"integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -3158,9 +3176,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/nanoid": {
|
"node_modules/nanoid": {
|
||||||
"version": "3.3.11",
|
"version": "3.3.12",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
|
||||||
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
"integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@@ -3334,9 +3352,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.5.14",
|
"version": "8.5.15",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
|
||||||
"integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==",
|
"integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -3353,7 +3371,7 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.11",
|
"nanoid": "^3.3.12",
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.1.1",
|
||||||
"source-map-js": "^1.2.1"
|
"source-map-js": "^1.2.1"
|
||||||
},
|
},
|
||||||
@@ -3610,14 +3628,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rolldown": {
|
"node_modules/rolldown": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz",
|
||||||
"integrity": "sha512-yD986aXDESFGS95spT1LAv0jssywP4npMEjmMHyN2/5+eE8qQJUype2AaKkRiLgBgyD0LFlubwAht7VmY8rGoA==",
|
"integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@oxc-project/types": "=0.129.0",
|
"@oxc-project/types": "=0.133.0",
|
||||||
"@rolldown/pluginutils": "1.0.0"
|
"@rolldown/pluginutils": "^1.0.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"rolldown": "bin/cli.mjs"
|
"rolldown": "bin/cli.mjs"
|
||||||
@@ -3626,27 +3644,27 @@
|
|||||||
"node": "^20.19.0 || >=22.12.0"
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@rolldown/binding-android-arm64": "1.0.0",
|
"@rolldown/binding-android-arm64": "1.0.3",
|
||||||
"@rolldown/binding-darwin-arm64": "1.0.0",
|
"@rolldown/binding-darwin-arm64": "1.0.3",
|
||||||
"@rolldown/binding-darwin-x64": "1.0.0",
|
"@rolldown/binding-darwin-x64": "1.0.3",
|
||||||
"@rolldown/binding-freebsd-x64": "1.0.0",
|
"@rolldown/binding-freebsd-x64": "1.0.3",
|
||||||
"@rolldown/binding-linux-arm-gnueabihf": "1.0.0",
|
"@rolldown/binding-linux-arm-gnueabihf": "1.0.3",
|
||||||
"@rolldown/binding-linux-arm64-gnu": "1.0.0",
|
"@rolldown/binding-linux-arm64-gnu": "1.0.3",
|
||||||
"@rolldown/binding-linux-arm64-musl": "1.0.0",
|
"@rolldown/binding-linux-arm64-musl": "1.0.3",
|
||||||
"@rolldown/binding-linux-ppc64-gnu": "1.0.0",
|
"@rolldown/binding-linux-ppc64-gnu": "1.0.3",
|
||||||
"@rolldown/binding-linux-s390x-gnu": "1.0.0",
|
"@rolldown/binding-linux-s390x-gnu": "1.0.3",
|
||||||
"@rolldown/binding-linux-x64-gnu": "1.0.0",
|
"@rolldown/binding-linux-x64-gnu": "1.0.3",
|
||||||
"@rolldown/binding-linux-x64-musl": "1.0.0",
|
"@rolldown/binding-linux-x64-musl": "1.0.3",
|
||||||
"@rolldown/binding-openharmony-arm64": "1.0.0",
|
"@rolldown/binding-openharmony-arm64": "1.0.3",
|
||||||
"@rolldown/binding-wasm32-wasi": "1.0.0",
|
"@rolldown/binding-wasm32-wasi": "1.0.3",
|
||||||
"@rolldown/binding-win32-arm64-msvc": "1.0.0",
|
"@rolldown/binding-win32-arm64-msvc": "1.0.3",
|
||||||
"@rolldown/binding-win32-x64-msvc": "1.0.0"
|
"@rolldown/binding-win32-x64-msvc": "1.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rolldown/node_modules/@rolldown/pluginutils": {
|
"node_modules/rolldown/node_modules/@rolldown/pluginutils": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
|
||||||
"integrity": "sha512-aKs/3GSWyV0mrhNmt/96/Z3yczC3yvrzYATCiCXQebBsGyYzjNdUphRVLeJQ67ySKVXRfMxt2lm12pmXvbPFQQ==",
|
"integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
@@ -4325,9 +4343,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tinyglobby": {
|
"node_modules/tinyglobby": {
|
||||||
"version": "0.2.16",
|
"version": "0.2.17",
|
||||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
|
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
|
||||||
"integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==",
|
"integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -4465,17 +4483,17 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "8.0.12",
|
"version": "8.0.16",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.12.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz",
|
||||||
"integrity": "sha512-w2dDofOWv2QB09ZITZBsvKTVAlYvPR4IAmrY/v0ir9KvLs0xybR7i48wxhM1/oyBWO34wPns+bPGw5ZrZqDpZg==",
|
"integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lightningcss": "^1.32.0",
|
"lightningcss": "^1.32.0",
|
||||||
"picomatch": "^4.0.4",
|
"picomatch": "^4.0.4",
|
||||||
"postcss": "^8.5.14",
|
"postcss": "^8.5.15",
|
||||||
"rolldown": "1.0.0",
|
"rolldown": "1.0.3",
|
||||||
"tinyglobby": "^0.2.16"
|
"tinyglobby": "^0.2.17"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"vite": "bin/vite.js"
|
"vite": "bin/vite.js"
|
||||||
|
|||||||
+7
-8
@@ -29,12 +29,12 @@ classifiers = [
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"arabic-reshaper==3.0.1", # Support for Arabic in reportlab
|
"arabic-reshaper==3.0.1", # Support for Arabic in reportlab
|
||||||
"babel",
|
"babel",
|
||||||
"BeautifulSoup4==4.14.*",
|
"BeautifulSoup4==4.15.*",
|
||||||
"bleach==6.4.*",
|
"bleach==6.4.*",
|
||||||
"celery==5.6.*",
|
"celery==5.6.*",
|
||||||
"chardet==5.2.*",
|
"chardet==5.2.*",
|
||||||
"cryptography>=48.0.0",
|
"cryptography>=49.0.0",
|
||||||
"css-inline==0.20.*",
|
"css-inline==0.21.*",
|
||||||
"defusedcsv>=3.0.0",
|
"defusedcsv>=3.0.0",
|
||||||
"dnspython==2.*",
|
"dnspython==2.*",
|
||||||
"Django[argon2]==5.2.*",
|
"Django[argon2]==5.2.*",
|
||||||
@@ -53,6 +53,7 @@ dependencies = [
|
|||||||
"django-oauth-toolkit==2.3.*",
|
"django-oauth-toolkit==2.3.*",
|
||||||
"django-otp==1.7.*",
|
"django-otp==1.7.*",
|
||||||
"django-phonenumber-field==8.4.*",
|
"django-phonenumber-field==8.4.*",
|
||||||
|
"django-querytagger==0.0.3",
|
||||||
"django-redis==6.0.*",
|
"django-redis==6.0.*",
|
||||||
"django-scopes==2.0.*",
|
"django-scopes==2.0.*",
|
||||||
"django-statici18n==2.7.*",
|
"django-statici18n==2.7.*",
|
||||||
@@ -93,7 +94,7 @@ dependencies = [
|
|||||||
"redis==7.4.*",
|
"redis==7.4.*",
|
||||||
"reportlab==4.5.*",
|
"reportlab==4.5.*",
|
||||||
"requests==2.32.*",
|
"requests==2.32.*",
|
||||||
"sentry-sdk==2.61.*",
|
"sentry-sdk==2.64.*",
|
||||||
"sepaxml==2.7.*",
|
"sepaxml==2.7.*",
|
||||||
"stripe==7.9.*",
|
"stripe==7.9.*",
|
||||||
"text-unidecode==1.*",
|
"text-unidecode==1.*",
|
||||||
@@ -101,7 +102,7 @@ dependencies = [
|
|||||||
"tqdm==4.*",
|
"tqdm==4.*",
|
||||||
"ua-parser==1.0.*",
|
"ua-parser==1.0.*",
|
||||||
"vobject==0.9.*",
|
"vobject==0.9.*",
|
||||||
"webauthn==2.7.*",
|
"webauthn==2.8.*",
|
||||||
"zeep==4.3.*"
|
"zeep==4.3.*"
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -125,7 +126,7 @@ dev = [
|
|||||||
"pytest-sugar",
|
"pytest-sugar",
|
||||||
"pytest-xdist==3.8.*",
|
"pytest-xdist==3.8.*",
|
||||||
"pytest-playwright",
|
"pytest-playwright",
|
||||||
"pytest==9.0.*",
|
"pytest==9.1.*",
|
||||||
"playwright",
|
"playwright",
|
||||||
"responses",
|
"responses",
|
||||||
]
|
]
|
||||||
@@ -139,8 +140,6 @@ build-backend = "backend"
|
|||||||
backend-path = ["_build"]
|
backend-path = ["_build"]
|
||||||
requires = [
|
requires = [
|
||||||
"setuptools",
|
"setuptools",
|
||||||
"setuptools-rust",
|
|
||||||
"wheel",
|
|
||||||
"importlib_metadata",
|
"importlib_metadata",
|
||||||
"tomli",
|
"tomli",
|
||||||
]
|
]
|
||||||
|
|||||||
+2
-2
@@ -6,8 +6,8 @@ localecompile:
|
|||||||
./manage.py compilemessages
|
./manage.py compilemessages
|
||||||
|
|
||||||
localegen:
|
localegen:
|
||||||
./manage.py makemessages --keep-pot --ignore "pretix/static/npm_dir/*" $(LNGS)
|
./manage.py makemessages --keep-pot --add-location file --ignore "pretix/static/npm_dir/*" $(LNGS)
|
||||||
./manage.py makemessages --keep-pot -d djangojs --ignore "pretix/static/npm_dir/*" --ignore "pretix/helpers/*" --ignore "pretix/static/jsi18n/*" --ignore "pretix/static/jsi18n/*" --ignore "pretix/static.dist/*" --ignore "data/*" --ignore "pretix/static/rrule/*" --ignore "build/*" $(LNGS)
|
./manage.py makemessages --keep-pot --add-location file -e js,ts,vue -d djangojs --ignore "pretix/static/npm_dir/*" --ignore "pretix/helpers/*" --ignore "pretix/static/jsi18n/*" --ignore "pretix/static/jsi18n/*" --ignore "pretix/static.dist/*" --ignore "data/*" --ignore "pretix/static/rrule/*" --ignore "build/*" $(LNGS)
|
||||||
|
|
||||||
staticfiles: npminstall npmbuild jsi18n
|
staticfiles: npminstall npmbuild jsi18n
|
||||||
./manage.py collectstatic --noinput
|
./manage.py collectstatic --noinput
|
||||||
|
|||||||
@@ -19,4 +19,4 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||||
# <https://www.gnu.org/licenses/>.
|
# <https://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
__version__ = "2026.6.0.dev0"
|
__version__ = "2026.7.0.dev0"
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ ALL_LANGUAGES = [
|
|||||||
('sv', _('Swedish')),
|
('sv', _('Swedish')),
|
||||||
('es', _('Spanish')),
|
('es', _('Spanish')),
|
||||||
('es-419', _('Spanish (Latin America)')),
|
('es-419', _('Spanish (Latin America)')),
|
||||||
|
('th', _('Thai')),
|
||||||
('tr', _('Turkish')),
|
('tr', _('Turkish')),
|
||||||
('uk', _('Ukrainian')),
|
('uk', _('Ukrainian')),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -110,6 +110,8 @@ class PretixScanSecurityProfile(AllowListSecurityProfile):
|
|||||||
('POST', 'api-v1:checkinrpc.redeem'),
|
('POST', 'api-v1:checkinrpc.redeem'),
|
||||||
('GET', 'api-v1:checkinrpc.search'),
|
('GET', 'api-v1:checkinrpc.search'),
|
||||||
('GET', 'api-v1:reusablemedium-list'),
|
('GET', 'api-v1:reusablemedium-list'),
|
||||||
|
('POST', 'api-v1:reusablemedium-lookup'),
|
||||||
|
('PATCH', 'api-v1:reusablemedium-detail')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -88,11 +88,19 @@ class CheckinRPCRedeemInputSerializer(serializers.Serializer):
|
|||||||
nonce = serializers.CharField(required=False, allow_null=True)
|
nonce = serializers.CharField(required=False, allow_null=True)
|
||||||
datetime = serializers.DateTimeField(required=False, allow_null=True)
|
datetime = serializers.DateTimeField(required=False, allow_null=True)
|
||||||
answers = serializers.JSONField(required=False, allow_null=True)
|
answers = serializers.JSONField(required=False, allow_null=True)
|
||||||
|
exchange_medium_type = serializers.ChoiceField(required=False, choices=MEDIA_TYPES)
|
||||||
|
exchange_medium_identifier = serializers.CharField(required=False)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.fields['lists'].child_relation.queryset = CheckinList.objects.filter(event__in=self.context['events']).select_related('event')
|
self.fields['lists'].child_relation.queryset = CheckinList.objects.filter(event__in=self.context['events']).select_related('event')
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
exchange_fields = ["exchange_medium_type", "exchange_medium_identifier"]
|
||||||
|
if any(attrs.get(k) is None for k in exchange_fields) and not all(attrs.get(k) is None for k in exchange_fields):
|
||||||
|
raise ValidationError("If you set any of exchange_medium_type or exchange_medium_identifier, you need to set both of them.")
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
class MiniCheckinListSerializer(I18nAwareModelSerializer):
|
class MiniCheckinListSerializer(I18nAwareModelSerializer):
|
||||||
event = serializers.SlugRelatedField(slug_field='slug', read_only=True)
|
event = serializers.SlugRelatedField(slug_field='slug', read_only=True)
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ from pretix.base.settings import (
|
|||||||
LazyI18nStringList, validate_event_settings,
|
LazyI18nStringList, validate_event_settings,
|
||||||
)
|
)
|
||||||
from pretix.base.signals import api_event_settings_fields
|
from pretix.base.signals import api_event_settings_fields
|
||||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
from pretix.multidomain.urlreverse import eventreverse_absolute
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -173,7 +173,7 @@ class EventSerializer(SalesChannelMigrationMixin, I18nAwareModelSerializer):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def get_event_url(self, event):
|
def get_event_url(self, event):
|
||||||
return build_absolute_uri(event, 'presale:event.index')
|
return eventreverse_absolute(event, 'presale:event.index')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Event
|
model = Event
|
||||||
@@ -871,6 +871,7 @@ class EventSettingsSerializer(SettingsSerializer):
|
|||||||
'og_image',
|
'og_image',
|
||||||
'name_scheme',
|
'name_scheme',
|
||||||
'reusable_media_active',
|
'reusable_media_active',
|
||||||
|
'reusable_media_usage_enforced',
|
||||||
'reusable_media_type_barcode',
|
'reusable_media_type_barcode',
|
||||||
'reusable_media_type_barcode_identifier_length',
|
'reusable_media_type_barcode_identifier_length',
|
||||||
'reusable_media_type_nfc_uid',
|
'reusable_media_type_nfc_uid',
|
||||||
@@ -885,6 +886,7 @@ class EventSettingsSerializer(SettingsSerializer):
|
|||||||
readonly_fields = [
|
readonly_fields = [
|
||||||
# These are read-only since they are currently only settable on organizers, not events
|
# These are read-only since they are currently only settable on organizers, not events
|
||||||
'reusable_media_active',
|
'reusable_media_active',
|
||||||
|
'reusable_media_usage_enforced',
|
||||||
'reusable_media_type_barcode',
|
'reusable_media_type_barcode',
|
||||||
'reusable_media_type_barcode_identifier_length',
|
'reusable_media_type_barcode_identifier_length',
|
||||||
'reusable_media_type_nfc_uid',
|
'reusable_media_type_nfc_uid',
|
||||||
@@ -970,6 +972,7 @@ class DeviceEventSettingsSerializer(EventSettingsSerializer):
|
|||||||
'reusable_media_type_nfc_uid',
|
'reusable_media_type_nfc_uid',
|
||||||
'reusable_media_type_nfc_mf0aes',
|
'reusable_media_type_nfc_mf0aes',
|
||||||
'reusable_media_type_nfc_mf0aes_random_uid',
|
'reusable_media_type_nfc_mf0aes_random_uid',
|
||||||
|
'reusable_media_usage_enforced',
|
||||||
'system_question_order',
|
'system_question_order',
|
||||||
'tax_rule_payment',
|
'tax_rule_payment',
|
||||||
'tax_rule_cancellation',
|
'tax_rule_cancellation',
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ from pretix.base.settings import (
|
|||||||
)
|
)
|
||||||
from pretix.base.signals import register_ticket_outputs
|
from pretix.base.signals import register_ticket_outputs
|
||||||
from pretix.helpers.countries import CachedCountries
|
from pretix.helpers.countries import CachedCountries
|
||||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
from pretix.multidomain.urlreverse import eventreverse_absolute
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -757,7 +757,7 @@ class PaymentURLField(serializers.URLField):
|
|||||||
def to_representation(self, instance: OrderPayment):
|
def to_representation(self, instance: OrderPayment):
|
||||||
if instance.state != OrderPayment.PAYMENT_STATE_CREATED:
|
if instance.state != OrderPayment.PAYMENT_STATE_CREATED:
|
||||||
return None
|
return None
|
||||||
return build_absolute_uri(instance.order.event, 'presale:event.order.pay', kwargs={
|
return eventreverse_absolute(instance.order.event, 'presale:event.order.pay', kwargs={
|
||||||
'order': instance.order.code,
|
'order': instance.order.code,
|
||||||
'secret': instance.order.secret,
|
'secret': instance.order.secret,
|
||||||
'payment': instance.pk,
|
'payment': instance.pk,
|
||||||
@@ -806,7 +806,7 @@ class OrderRefundSerializer(I18nAwareModelSerializer):
|
|||||||
|
|
||||||
class OrderURLField(serializers.URLField):
|
class OrderURLField(serializers.URLField):
|
||||||
def to_representation(self, instance: Order):
|
def to_representation(self, instance: Order):
|
||||||
return build_absolute_uri(instance.event, 'presale:event.order', kwargs={
|
return eventreverse_absolute(instance.event, 'presale:event.order', kwargs={
|
||||||
'order': instance.code,
|
'order': instance.code,
|
||||||
'secret': instance.secret,
|
'secret': instance.secret,
|
||||||
})
|
})
|
||||||
@@ -1043,15 +1043,13 @@ class OrderPositionCreateSerializer(I18nAwareModelSerializer):
|
|||||||
requested_valid_from = serializers.DateTimeField(required=False, allow_null=True)
|
requested_valid_from = serializers.DateTimeField(required=False, allow_null=True)
|
||||||
use_reusable_medium = serializers.PrimaryKeyRelatedField(queryset=ReusableMedium.objects.none(),
|
use_reusable_medium = serializers.PrimaryKeyRelatedField(queryset=ReusableMedium.objects.none(),
|
||||||
required=False, allow_null=True)
|
required=False, allow_null=True)
|
||||||
add_to_reusable_medium = serializers.PrimaryKeyRelatedField(queryset=ReusableMedium.objects.none(),
|
|
||||||
required=False, allow_null=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = OrderPosition
|
model = OrderPosition
|
||||||
fields = ('positionid', 'item', 'variation', 'price', 'attendee_name', 'attendee_name_parts', 'attendee_email',
|
fields = ('positionid', 'item', 'variation', 'price', 'attendee_name', 'attendee_name_parts', 'attendee_email',
|
||||||
'company', 'street', 'zipcode', 'city', 'country', 'state', 'is_bundled',
|
'company', 'street', 'zipcode', 'city', 'country', 'state', 'is_bundled',
|
||||||
'secret', 'addon_to', 'subevent', 'answers', 'seat', 'voucher', 'valid_from', 'valid_until',
|
'secret', 'addon_to', 'subevent', 'answers', 'seat', 'voucher', 'valid_from', 'valid_until',
|
||||||
'requested_valid_from', 'use_reusable_medium', 'add_to_reusable_medium', 'discount')
|
'requested_valid_from', 'use_reusable_medium', 'discount')
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
@@ -1063,8 +1061,6 @@ class OrderPositionCreateSerializer(I18nAwareModelSerializer):
|
|||||||
with scopes_disabled():
|
with scopes_disabled():
|
||||||
if 'use_reusable_medium' in self.fields:
|
if 'use_reusable_medium' in self.fields:
|
||||||
self.fields['use_reusable_medium'].queryset = ReusableMedium.objects.all()
|
self.fields['use_reusable_medium'].queryset = ReusableMedium.objects.all()
|
||||||
if 'add_to_reusable_medium' in self.fields:
|
|
||||||
self.fields['add_to_reusable_medium'].queryset = ReusableMedium.objects.all()
|
|
||||||
|
|
||||||
def validate_secret(self, secret):
|
def validate_secret(self, secret):
|
||||||
if secret and OrderPosition.all.filter(order__event=self.context['event'], secret=secret).exists():
|
if secret and OrderPosition.all.filter(order__event=self.context['event'], secret=secret).exists():
|
||||||
@@ -1080,9 +1076,6 @@ class OrderPositionCreateSerializer(I18nAwareModelSerializer):
|
|||||||
)
|
)
|
||||||
return m
|
return m
|
||||||
|
|
||||||
def validate_add_to_reusable_medium(self, m):
|
|
||||||
return self.validate_use_reusable_medium(m)
|
|
||||||
|
|
||||||
def validate_item(self, item):
|
def validate_item(self, item):
|
||||||
if item.event != self.context['event']:
|
if item.event != self.context['event']:
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
@@ -1157,12 +1150,6 @@ class OrderPositionCreateSerializer(I18nAwareModelSerializer):
|
|||||||
{'discount': ['You can only specify a discount if you do the price computation, but price is not set.']}
|
{'discount': ['You can only specify a discount if you do the price computation, but price is not set.']}
|
||||||
)
|
)
|
||||||
|
|
||||||
if 'use_reusable_medium' in data and 'add_to_reusable_medium' in data:
|
|
||||||
raise ValidationError({
|
|
||||||
'use_reusable_medium': ['You can only specify either use_reusable_medium or add_to_reusable_medium.'],
|
|
||||||
'add_to_reusable_medium': ['You can only specify either use_reusable_medium or add_to_reusable_medium.'],
|
|
||||||
})
|
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
@@ -1602,7 +1589,7 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
|
|||||||
pos_data['attendee_name_parts'] = {
|
pos_data['attendee_name_parts'] = {
|
||||||
'_legacy': attendee_name
|
'_legacy': attendee_name
|
||||||
}
|
}
|
||||||
pos = OrderPosition(**{k: v for k, v in pos_data.items() if k not in ('answers', '_quotas', 'use_reusable_medium', 'add_to_reusable_medium')})
|
pos = OrderPosition(**{k: v for k, v in pos_data.items() if k not in ('answers', '_quotas', 'use_reusable_medium')})
|
||||||
if simulate:
|
if simulate:
|
||||||
pos.order = order._wrapped
|
pos.order = order._wrapped
|
||||||
else:
|
else:
|
||||||
@@ -1676,7 +1663,6 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
|
|||||||
for pos_data in positions_data:
|
for pos_data in positions_data:
|
||||||
answers_data = pos_data.pop('answers', [])
|
answers_data = pos_data.pop('answers', [])
|
||||||
use_reusable_medium = pos_data.pop('use_reusable_medium', None)
|
use_reusable_medium = pos_data.pop('use_reusable_medium', None)
|
||||||
add_to_reusable_medium = pos_data.pop('add_to_reusable_medium', None)
|
|
||||||
pos = pos_data['__instance']
|
pos = pos_data['__instance']
|
||||||
pos._calculate_tax(invoice_address=ia)
|
pos._calculate_tax(invoice_address=ia)
|
||||||
|
|
||||||
@@ -1718,14 +1704,17 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
|
|||||||
answ.options.add(*options)
|
answ.options.add(*options)
|
||||||
|
|
||||||
if use_reusable_medium:
|
if use_reusable_medium:
|
||||||
for op_pk in use_reusable_medium.linked_orderpositions.values_list('pk', flat=True):
|
if pos.item.media_policy not in (Item.MEDIA_POLICY_APPEND, Item.MEDIA_POLICY_APPEND_OR_NEW):
|
||||||
use_reusable_medium.log_action(
|
for op_pk in use_reusable_medium.linked_orderpositions.values_list('pk', flat=True):
|
||||||
'pretix.reusable_medium.linked_orderposition.removed',
|
use_reusable_medium.log_action(
|
||||||
data={
|
'pretix.reusable_medium.linked_orderposition.removed',
|
||||||
'linked_orderposition': op_pk,
|
data={
|
||||||
}
|
'linked_orderposition': op_pk,
|
||||||
)
|
}
|
||||||
use_reusable_medium.linked_orderpositions.set([pos])
|
)
|
||||||
|
use_reusable_medium.linked_orderpositions.set([pos])
|
||||||
|
else:
|
||||||
|
use_reusable_medium.linked_orderpositions.add(pos)
|
||||||
use_reusable_medium.log_action(
|
use_reusable_medium.log_action(
|
||||||
'pretix.reusable_medium.linked_orderposition.added',
|
'pretix.reusable_medium.linked_orderposition.added',
|
||||||
data={
|
data={
|
||||||
@@ -1733,15 +1722,7 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
|
|||||||
'linked_orderposition': pos.pk,
|
'linked_orderposition': pos.pk,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
elif add_to_reusable_medium:
|
use_reusable_medium.touch()
|
||||||
add_to_reusable_medium.linked_orderpositions.add(pos)
|
|
||||||
add_to_reusable_medium.log_action(
|
|
||||||
'pretix.reusable_medium.linked_orderposition.added',
|
|
||||||
data={
|
|
||||||
'by_order': order.code,
|
|
||||||
'linked_orderposition': pos.pk,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
if not simulate:
|
if not simulate:
|
||||||
for cp in delete_cps:
|
for cp in delete_cps:
|
||||||
|
|||||||
@@ -58,8 +58,8 @@ from pretix.helpers.permission_migration import (
|
|||||||
OLD_TO_NEW_EVENT_COMPAT, OLD_TO_NEW_EVENT_MIGRATION,
|
OLD_TO_NEW_EVENT_COMPAT, OLD_TO_NEW_EVENT_MIGRATION,
|
||||||
OLD_TO_NEW_ORGANIZER_COMPAT, OLD_TO_NEW_ORGANIZER_MIGRATION,
|
OLD_TO_NEW_ORGANIZER_COMPAT, OLD_TO_NEW_ORGANIZER_MIGRATION,
|
||||||
)
|
)
|
||||||
from pretix.helpers.urls import build_absolute_uri as build_global_uri
|
from pretix.helpers.urls import mainreverse_absolute
|
||||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
from pretix.multidomain.urlreverse import eventreverse_absolute
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ class OrganizerSerializer(I18nAwareModelSerializer):
|
|||||||
slug = serializers.CharField(read_only=True)
|
slug = serializers.CharField(read_only=True)
|
||||||
|
|
||||||
def get_organizer_url(self, organizer):
|
def get_organizer_url(self, organizer):
|
||||||
return build_absolute_uri(organizer, 'presale:organizer.index')
|
return eventreverse_absolute(organizer, 'presale:organizer.index')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Organizer
|
model = Organizer
|
||||||
@@ -499,7 +499,7 @@ class TeamInviteSerializer(serializers.ModelSerializer):
|
|||||||
'user': self,
|
'user': self,
|
||||||
'organizer': self.context['organizer'].name,
|
'organizer': self.context['organizer'].name,
|
||||||
'team': instance.team.name,
|
'team': instance.team.name,
|
||||||
'url': build_global_uri('control:auth.invite', kwargs={
|
'url': mainreverse_absolute('control:auth.invite', kwargs={
|
||||||
'token': instance.token
|
'token': instance.token
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -605,6 +605,7 @@ class OrganizerSettingsSerializer(SettingsSerializer):
|
|||||||
'cookie_consent_dialog_button_yes',
|
'cookie_consent_dialog_button_yes',
|
||||||
'cookie_consent_dialog_button_no',
|
'cookie_consent_dialog_button_no',
|
||||||
'reusable_media_active',
|
'reusable_media_active',
|
||||||
|
'reusable_media_usage_enforced',
|
||||||
'reusable_media_type_barcode',
|
'reusable_media_type_barcode',
|
||||||
'reusable_media_type_barcode_identifier_length',
|
'reusable_media_type_barcode_identifier_length',
|
||||||
'reusable_media_type_nfc_uid',
|
'reusable_media_type_nfc_uid',
|
||||||
|
|||||||
@@ -69,8 +69,10 @@ from pretix.base.models import (
|
|||||||
from pretix.base.models.orders import PrintLog
|
from pretix.base.models.orders import PrintLog
|
||||||
from pretix.base.permissions import AnyPermissionOf
|
from pretix.base.permissions import AnyPermissionOf
|
||||||
from pretix.base.services.checkin import (
|
from pretix.base.services.checkin import (
|
||||||
CheckInError, RequiredQuestionsError, SQLLogic, perform_checkin,
|
CheckInError, RequiredMediaExchangeError, RequiredQuestionsError, SQLLogic,
|
||||||
|
perform_checkin,
|
||||||
)
|
)
|
||||||
|
from pretix.base.services.media import perform_media_exchange
|
||||||
from pretix.base.signals import checkin_annulled
|
from pretix.base.signals import checkin_annulled
|
||||||
from pretix.helpers import OF_SELF
|
from pretix.helpers import OF_SELF
|
||||||
|
|
||||||
@@ -454,7 +456,8 @@ def _checkin_list_position_queryset(checkinlists, ignore_status=False, ignore_pr
|
|||||||
|
|
||||||
def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force, checkin_type, ignore_unpaid, nonce,
|
def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force, checkin_type, ignore_unpaid, nonce,
|
||||||
untrusted_input, user, auth, expand, pdf_data, request, questions_supported, canceled_supported,
|
untrusted_input, user, auth, expand, pdf_data, request, questions_supported, canceled_supported,
|
||||||
source_type='barcode', legacy_url_support=False, simulate=False, gate=None, use_order_locale=False):
|
source_type='barcode', legacy_url_support=False, simulate=False, gate=None, use_order_locale=False,
|
||||||
|
exchange_medium_type=None, exchange_medium_identifier=None):
|
||||||
if not checkinlists:
|
if not checkinlists:
|
||||||
raise ValidationError('No check-in list passed.')
|
raise ValidationError('No check-in list passed.')
|
||||||
|
|
||||||
@@ -463,6 +466,7 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
|
|||||||
|
|
||||||
device = auth if isinstance(auth, Device) else None
|
device = auth if isinstance(auth, Device) else None
|
||||||
gate = gate or (auth.gate if isinstance(auth, Device) else None)
|
gate = gate or (auth.gate if isinstance(auth, Device) else None)
|
||||||
|
medium = None
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'request': request,
|
'request': request,
|
||||||
@@ -522,7 +526,7 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
|
|||||||
# with respecting the force option), or it's a reusable medium (-> proceed with that)
|
# with respecting the force option), or it's a reusable medium (-> proceed with that)
|
||||||
if not op_candidates:
|
if not op_candidates:
|
||||||
try:
|
try:
|
||||||
media = ReusableMedium.objects.active().filter(
|
medium = ReusableMedium.objects.active().filter(
|
||||||
Exists(ReusableMedium.linked_orderpositions.through.objects.filter(reusablemedium_id=OuterRef('pk')))
|
Exists(ReusableMedium.linked_orderpositions.through.objects.filter(reusablemedium_id=OuterRef('pk')))
|
||||||
).get(
|
).get(
|
||||||
organizer_id=checkinlists[0].event.organizer_id,
|
organizer_id=checkinlists[0].event.organizer_id,
|
||||||
@@ -630,7 +634,7 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
|
|||||||
'list': MiniCheckinListSerializer(list_by_event[revoked_matches[0].event_id]).data,
|
'list': MiniCheckinListSerializer(list_by_event[revoked_matches[0].event_id]).data,
|
||||||
}, status=400)
|
}, status=400)
|
||||||
else:
|
else:
|
||||||
linked_ops = media.linked_orderpositions.all().select_related("order").prefetch_related("addons")
|
linked_ops = medium.linked_orderpositions.all().select_related("order").prefetch_related("addons")
|
||||||
linked_event_ids = {op.order.event_id for op in linked_ops}
|
linked_event_ids = {op.order.event_id for op in linked_ops}
|
||||||
if not any(event_id in list_by_event for event_id in linked_event_ids):
|
if not any(event_id in list_by_event for event_id in linked_event_ids):
|
||||||
# Medium exists but connected ticket is for the wrong event
|
# Medium exists but connected ticket is for the wrong event
|
||||||
@@ -661,7 +665,7 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
|
|||||||
op_candidates = []
|
op_candidates = []
|
||||||
for op in linked_ops:
|
for op in linked_ops:
|
||||||
if op.order.event_id in list_by_event:
|
if op.order.event_id in list_by_event:
|
||||||
reusable_medium_used = media
|
reusable_medium_used = medium
|
||||||
op_candidates.append(op)
|
op_candidates.append(op)
|
||||||
if list_by_event[op.order.event_id].addon_match:
|
if list_by_event[op.order.event_id].addon_match:
|
||||||
op_candidates += list(op.addons.all())
|
op_candidates += list(op.addons.all())
|
||||||
@@ -804,7 +808,14 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
|
|||||||
locale = op.order.event.settings.locale
|
locale = op.order.event.settings.locale
|
||||||
with language(locale):
|
with language(locale):
|
||||||
try:
|
try:
|
||||||
perform_checkin(
|
if exchange_medium_identifier and medium:
|
||||||
|
# Cannot scan a medium and then request to exchange it
|
||||||
|
raise CheckInError(
|
||||||
|
gettext('You cannot exchange a medium for a medium.'),
|
||||||
|
'error'
|
||||||
|
)
|
||||||
|
|
||||||
|
checkin_args = dict(
|
||||||
op=op,
|
op=op,
|
||||||
clist=list_by_event[op.order.event_id],
|
clist=list_by_event[op.order.event_id],
|
||||||
given_answers=given_answers,
|
given_answers=given_answers,
|
||||||
@@ -822,7 +833,25 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
|
|||||||
from_revoked_secret=from_revoked_secret,
|
from_revoked_secret=from_revoked_secret,
|
||||||
simulate=simulate,
|
simulate=simulate,
|
||||||
gate=gate,
|
gate=gate,
|
||||||
|
reusable_medium=medium,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if exchange_medium_identifier: # other fields are filled, see CheckinRPCRedeemInputSerializer.validate
|
||||||
|
with transaction.atomic():
|
||||||
|
# Do exchange and check-in atomically, i.e. both succeed or both fail
|
||||||
|
medium = perform_media_exchange(
|
||||||
|
organizer=request.organizer,
|
||||||
|
media_type=exchange_medium_type,
|
||||||
|
identifier=exchange_medium_identifier,
|
||||||
|
link_orderposition=op,
|
||||||
|
user=user,
|
||||||
|
auth=auth,
|
||||||
|
)
|
||||||
|
source_type = medium.media_type.identifier
|
||||||
|
checkin_args['reusable_medium'] = medium
|
||||||
|
perform_checkin(**checkin_args)
|
||||||
|
else:
|
||||||
|
perform_checkin(**checkin_args)
|
||||||
except RequiredQuestionsError as e:
|
except RequiredQuestionsError as e:
|
||||||
return Response({
|
return Response({
|
||||||
'status': 'incomplete',
|
'status': 'incomplete',
|
||||||
@@ -834,6 +863,18 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
|
|||||||
],
|
],
|
||||||
'list': MiniCheckinListSerializer(list_by_event[op.order.event_id]).data,
|
'list': MiniCheckinListSerializer(list_by_event[op.order.event_id]).data,
|
||||||
}, status=400)
|
}, status=400)
|
||||||
|
except RequiredMediaExchangeError as e:
|
||||||
|
return Response({
|
||||||
|
'status': 'exchange',
|
||||||
|
'require_attention': op.require_checkin_attention,
|
||||||
|
'checkin_texts': op.checkin_texts,
|
||||||
|
'position': CheckinListOrderPositionSerializer(op, context=_make_context(context, op.order.event)).data,
|
||||||
|
'media_policy': e.media_policy,
|
||||||
|
'media_type': e.media_type,
|
||||||
|
'list': MiniCheckinListSerializer(list_by_event[op.order.event_id]).data,
|
||||||
|
'reason': e.code,
|
||||||
|
'reason_explanation': e.msg,
|
||||||
|
}, status=400)
|
||||||
except CheckInError as e:
|
except CheckInError as e:
|
||||||
if not simulate:
|
if not simulate:
|
||||||
op.order.log_action('pretix.event.checkin.denied', data={
|
op.order.log_action('pretix.event.checkin.denied', data={
|
||||||
@@ -1021,6 +1062,8 @@ class CheckinRPCRedeemView(views.APIView):
|
|||||||
canceled_supported=True,
|
canceled_supported=True,
|
||||||
request=self.request, # this is not clean, but we need it in the serializers for URL generation
|
request=self.request, # this is not clean, but we need it in the serializers for URL generation
|
||||||
legacy_url_support=False,
|
legacy_url_support=False,
|
||||||
|
exchange_medium_type=s.validated_data.get('exchange_medium_type'),
|
||||||
|
exchange_medium_identifier=s.validated_data.get('exchange_medium_identifier'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ class ReusableMediaViewSet(viewsets.ModelViewSet):
|
|||||||
|
|
||||||
return Response({"result": None})
|
return Response({"result": None})
|
||||||
|
|
||||||
@scopes_disabled() # we are sure enough that get_queryset() is correct, so we save some perforamnce
|
@scopes_disabled() # we are sure enough that get_queryset() is correct, so we save some performance
|
||||||
def list(self, request, **kwargs):
|
def list(self, request, **kwargs):
|
||||||
date = serializers.DateTimeField().to_representation(now())
|
date = serializers.DateTimeField().to_representation(now())
|
||||||
queryset = self.filter_queryset(self.get_queryset())
|
queryset = self.filter_queryset(self.get_queryset())
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ from django.core.exceptions import ValidationError
|
|||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from requests import RequestException
|
from requests import RequestException
|
||||||
|
|
||||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
from pretix.multidomain.urlreverse import eventreverse_absolute
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -313,7 +313,7 @@ def _get_or_create_server_keypair(organizer):
|
|||||||
|
|
||||||
def generate_id_token(customer, client, auth_time, nonce, scope, expires: datetime, scope_claims=False, with_code=None, with_access_token=None):
|
def generate_id_token(customer, client, auth_time, nonce, scope, expires: datetime, scope_claims=False, with_code=None, with_access_token=None):
|
||||||
payload = {
|
payload = {
|
||||||
'iss': build_absolute_uri(client.organizer, 'presale:organizer.index').rstrip('/'),
|
'iss': eventreverse_absolute(client.organizer, 'presale:organizer.index').rstrip('/'),
|
||||||
'aud': client.client_id,
|
'aud': client.client_id,
|
||||||
'exp': int(expires.timestamp()),
|
'exp': int(expires.timestamp()),
|
||||||
'iat': int(time.time()),
|
'iat': int(time.time()),
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ from django.utils.translation import gettext_lazy as _, pgettext_lazy
|
|||||||
|
|
||||||
from pretix.base.models import Checkin, InvoiceAddress, Order, Question
|
from pretix.base.models import Checkin, InvoiceAddress, Order, Question
|
||||||
from pretix.base.settings import PERSON_NAME_SCHEMES
|
from pretix.base.settings import PERSON_NAME_SCHEMES
|
||||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
from pretix.multidomain.urlreverse import eventreverse_absolute
|
||||||
|
|
||||||
|
|
||||||
def get_answer(op, question_identifier=None):
|
def get_answer(op, question_identifier=None):
|
||||||
@@ -545,7 +545,7 @@ def get_data_fields(event, for_model=None):
|
|||||||
_("Order link"),
|
_("Order link"),
|
||||||
Question.TYPE_STRING,
|
Question.TYPE_STRING,
|
||||||
None,
|
None,
|
||||||
lambda order: build_absolute_uri(
|
lambda order: eventreverse_absolute(
|
||||||
event,
|
event,
|
||||||
'presale:event.order', kwargs={
|
'presale:event.order', kwargs={
|
||||||
'order': order.code,
|
'order': order.code,
|
||||||
@@ -560,7 +560,7 @@ def get_data_fields(event, for_model=None):
|
|||||||
_("Ticket link"),
|
_("Ticket link"),
|
||||||
Question.TYPE_STRING,
|
Question.TYPE_STRING,
|
||||||
None,
|
None,
|
||||||
lambda op: build_absolute_uri(
|
lambda op: eventreverse_absolute(
|
||||||
event,
|
event,
|
||||||
'presale:event.order.position', kwargs={
|
'presale:event.order.position', kwargs={
|
||||||
'order': op.order.code,
|
'order': op.order.code,
|
||||||
|
|||||||
@@ -57,6 +57,8 @@ logger = logging.getLogger('pretix.base.email')
|
|||||||
|
|
||||||
T = TypeVar("T", bound=EmailBackend)
|
T = TypeVar("T", bound=EmailBackend)
|
||||||
|
|
||||||
|
_cgnat_net = ipaddress.ip_network('100.64.0.0/10')
|
||||||
|
|
||||||
|
|
||||||
def test_custom_smtp_backend(backend: T, from_addr: str) -> None:
|
def test_custom_smtp_backend(backend: T, from_addr: str) -> None:
|
||||||
try:
|
try:
|
||||||
@@ -253,12 +255,15 @@ def create_connection(address, timeout=socket.getdefaulttimeout(),
|
|||||||
|
|
||||||
if not getattr(settings, "MAIL_CUSTOM_SMTP_ALLOW_PRIVATE_NETWORKS", False):
|
if not getattr(settings, "MAIL_CUSTOM_SMTP_ALLOW_PRIVATE_NETWORKS", False):
|
||||||
ip_addr = ipaddress.ip_address(sa[0])
|
ip_addr = ipaddress.ip_address(sa[0])
|
||||||
|
check_ip4 = ip_addr.ipv4_mapped if getattr(ip_addr, "ipv4_mapped", None) else ip_addr
|
||||||
if ip_addr.is_multicast:
|
if ip_addr.is_multicast:
|
||||||
raise socket.error(f"Request to multicast address {sa[0]} blocked")
|
raise socket.error(f"Request to multicast address {sa[0]} blocked")
|
||||||
if ip_addr.is_loopback or ip_addr.is_link_local:
|
if ip_addr.is_loopback or ip_addr.is_link_local:
|
||||||
raise socket.error(f"Request to local address {sa[0]} blocked")
|
raise socket.error(f"Request to local address {sa[0]} blocked")
|
||||||
if ip_addr.is_private:
|
if ip_addr.is_private:
|
||||||
raise socket.error(f"Request to private address {sa[0]} blocked")
|
raise socket.error(f"Request to private address {sa[0]} blocked")
|
||||||
|
if check_ip4 in _cgnat_net:
|
||||||
|
raise socket.error(f"Request to RFC 6598 address {sa[0]} blocked")
|
||||||
|
|
||||||
sock = None
|
sock = None
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ from ...control.forms.filter import get_all_payment_providers
|
|||||||
from ...helpers import GroupConcat
|
from ...helpers import GroupConcat
|
||||||
from ...helpers.iter import chunked_iterable
|
from ...helpers.iter import chunked_iterable
|
||||||
from ...helpers.safe_openpyxl import remove_invalid_excel_chars
|
from ...helpers.safe_openpyxl import remove_invalid_excel_chars
|
||||||
from ...multidomain.urlreverse import build_absolute_uri
|
from ...multidomain.urlreverse import eventreverse_absolute
|
||||||
from ..exporter import (
|
from ..exporter import (
|
||||||
ListExporter, MultiSheetListExporter, OrganizerLevelExportMixin,
|
ListExporter, MultiSheetListExporter, OrganizerLevelExportMixin,
|
||||||
)
|
)
|
||||||
@@ -429,7 +429,7 @@ class OrderListExporter(MultiSheetListExporter):
|
|||||||
]))
|
]))
|
||||||
|
|
||||||
row.append(
|
row.append(
|
||||||
build_absolute_uri(order.event, 'presale:event.order', kwargs={
|
eventreverse_absolute(order.event, 'presale:event.order', kwargs={
|
||||||
'order': order.code,
|
'order': order.code,
|
||||||
'secret': order.secret,
|
'secret': order.secret,
|
||||||
})
|
})
|
||||||
@@ -855,7 +855,7 @@ class OrderListExporter(MultiSheetListExporter):
|
|||||||
]))
|
]))
|
||||||
|
|
||||||
row.append(
|
row.append(
|
||||||
build_absolute_uri(order.event, 'presale:event.order.position', kwargs={
|
eventreverse_absolute(order.event, 'presale:event.order.position', kwargs={
|
||||||
'order': order.code,
|
'order': order.code,
|
||||||
'secret': op.web_secret,
|
'secret': op.web_secret,
|
||||||
'position': op.positionid
|
'position': op.positionid
|
||||||
|
|||||||
@@ -22,9 +22,7 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
import re
|
|
||||||
import textwrap
|
import textwrap
|
||||||
import unicodedata
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
@@ -58,8 +56,8 @@ from pretix.base.services.currencies import SOURCE_NAMES
|
|||||||
from pretix.base.signals import register_invoice_renderers
|
from pretix.base.signals import register_invoice_renderers
|
||||||
from pretix.base.templatetags.money import money_filter
|
from pretix.base.templatetags.money import money_filter
|
||||||
from pretix.helpers.reportlab import (
|
from pretix.helpers.reportlab import (
|
||||||
FontFallbackParagraph, ThumbnailingImageReader, register_ttf_font_if_new,
|
FontFallbackParagraph, PlainTextParagraph, ThumbnailingImageReader,
|
||||||
reshaper,
|
normalize_text, register_ttf_font_if_new, reshaper,
|
||||||
)
|
)
|
||||||
from pretix.presale.style import get_fonts
|
from pretix.presale.style import get_fonts
|
||||||
|
|
||||||
@@ -259,18 +257,8 @@ class BaseReportlabInvoiceRenderer(BaseInvoiceRenderer):
|
|||||||
register_ttf_font_if_new(family + ' B I', finders.find(styles['bolditalic']['truetype']))
|
register_ttf_font_if_new(family + ' B I', finders.find(styles['bolditalic']['truetype']))
|
||||||
|
|
||||||
def _normalize(self, text):
|
def _normalize(self, text):
|
||||||
# reportlab does not support unicode combination characters
|
# alias kept for plugin compatibility
|
||||||
# It's important we do this before we use ArabicReshaper
|
return normalize_text(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.
|
|
||||||
try:
|
|
||||||
text = "<br />".join(get_display(reshaper.reshape(l)) for l in re.split("<br ?/>", text))
|
|
||||||
except:
|
|
||||||
logger.exception('Reshaping/Bidi fixes failed on string {}'.format(repr(text)))
|
|
||||||
|
|
||||||
return text
|
|
||||||
|
|
||||||
def _upper(self, val):
|
def _upper(self, val):
|
||||||
# We uppercase labels, but not in every language
|
# We uppercase labels, but not in every language
|
||||||
@@ -351,10 +339,15 @@ class BaseReportlabInvoiceRenderer(BaseInvoiceRenderer):
|
|||||||
return 'invoice.pdf', 'application/pdf', buffer.read()
|
return 'invoice.pdf', 'application/pdf', buffer.read()
|
||||||
|
|
||||||
def _clean_text(self, text, tags=None):
|
def _clean_text(self, text, tags=None):
|
||||||
return self._normalize(bleach.clean(
|
# For backwards compatibility with customer content, we need to support tags like <br> and <b> in a few text
|
||||||
text,
|
# fields. Therefore, we can't use PlainTextParagraph for these, but run bleach instead to limit the allowed
|
||||||
tags=set(tags) if tags else set()
|
# tags.
|
||||||
).strip().replace('<br>', '<br />').replace('\n', '<br />\n'))
|
return self._normalize(
|
||||||
|
bleach.clean(
|
||||||
|
text,
|
||||||
|
tags=set(tags) if tags else set()
|
||||||
|
).strip().replace('<br>', '<br />').replace('\n', '<br />\n')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class PaidMarker(Flowable):
|
class PaidMarker(Flowable):
|
||||||
@@ -405,8 +398,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
invoice_to_top = 52 * mm
|
invoice_to_top = 52 * mm
|
||||||
|
|
||||||
def _draw_invoice_to(self, canvas):
|
def _draw_invoice_to(self, canvas):
|
||||||
p = FontFallbackParagraph(self._clean_text(self.invoice.address_invoice_to),
|
p = PlainTextParagraph(self.invoice.address_invoice_to, style=self.stylesheet['Normal'])
|
||||||
style=self.stylesheet['Normal'])
|
|
||||||
p.wrapOn(canvas, self.invoice_to_width, self.invoice_to_height)
|
p.wrapOn(canvas, self.invoice_to_width, self.invoice_to_height)
|
||||||
p_size = p.wrap(self.invoice_to_width, self.invoice_to_height)
|
p_size = p.wrap(self.invoice_to_width, self.invoice_to_height)
|
||||||
p.drawOn(canvas, self.invoice_to_left, self.pagesize[1] - p_size[1] - self.invoice_to_top)
|
p.drawOn(canvas, self.invoice_to_left, self.pagesize[1] - p_size[1] - self.invoice_to_top)
|
||||||
@@ -417,8 +409,8 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
invoice_from_top = 17 * mm
|
invoice_from_top = 17 * mm
|
||||||
|
|
||||||
def _draw_invoice_from(self, canvas):
|
def _draw_invoice_from(self, canvas):
|
||||||
p = FontFallbackParagraph(
|
p = PlainTextParagraph(
|
||||||
self._clean_text(self.invoice.full_invoice_from),
|
self.invoice.full_invoice_from,
|
||||||
style=self.stylesheet['InvoiceFrom']
|
style=self.stylesheet['InvoiceFrom']
|
||||||
)
|
)
|
||||||
p.wrapOn(canvas, self.invoice_from_width, self.invoice_from_height)
|
p.wrapOn(canvas, self.invoice_from_width, self.invoice_from_height)
|
||||||
@@ -548,13 +540,12 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
def _draw_event(self, canvas):
|
def _draw_event(self, canvas):
|
||||||
def shorten(txt):
|
def shorten(txt):
|
||||||
txt = str(txt)
|
txt = str(txt)
|
||||||
txt = bleach.clean(txt, tags=set()).strip()
|
p = PlainTextParagraph(txt, style=self.stylesheet['Normal'])
|
||||||
p = FontFallbackParagraph(self._normalize(txt.strip().replace('\n', '<br />\n')), style=self.stylesheet['Normal'])
|
|
||||||
p_size = p.wrap(self.event_width, self.event_height)
|
p_size = p.wrap(self.event_width, self.event_height)
|
||||||
|
|
||||||
while p_size[1] > 2 * self.stylesheet['Normal'].leading:
|
while p_size[1] > 2 * self.stylesheet['Normal'].leading:
|
||||||
txt = ' '.join(txt.replace('…', '').split()[:-1]) + '…'
|
txt = ' '.join(txt.replace('…', '').split()[:-1]) + '…'
|
||||||
p = FontFallbackParagraph(self._normalize(txt.strip().replace('\n', '<br />\n')), style=self.stylesheet['Normal'])
|
p = PlainTextParagraph(txt, style=self.stylesheet['Normal'])
|
||||||
p_size = p.wrap(self.event_width, self.event_height)
|
p_size = p.wrap(self.event_width, self.event_height)
|
||||||
return txt
|
return txt
|
||||||
|
|
||||||
@@ -572,7 +563,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
else:
|
else:
|
||||||
p_str = shorten(self.invoice.event.name)
|
p_str = shorten(self.invoice.event.name)
|
||||||
|
|
||||||
p = FontFallbackParagraph(self._normalize(p_str.strip().replace('\n', '<br />\n')), style=self.stylesheet['Normal'])
|
p = PlainTextParagraph(p_str, style=self.stylesheet['Normal'])
|
||||||
p.wrapOn(canvas, self.event_width, self.event_height)
|
p.wrapOn(canvas, self.event_width, self.event_height)
|
||||||
p_size = p.wrap(self.event_width, self.event_height)
|
p_size = p.wrap(self.event_width, self.event_height)
|
||||||
p.drawOn(canvas, self.event_left, self.pagesize[1] - self.event_top - p_size[1])
|
p.drawOn(canvas, self.event_left, self.pagesize[1] - self.event_top - p_size[1])
|
||||||
@@ -645,39 +636,37 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
|
|
||||||
type_info_text = self.invoice.transmission_type_instance.pdf_info_text()
|
type_info_text = self.invoice.transmission_type_instance.pdf_info_text()
|
||||||
if type_info_text:
|
if type_info_text:
|
||||||
story.append(FontFallbackParagraph(
|
story.append(PlainTextParagraph(
|
||||||
type_info_text,
|
type_info_text,
|
||||||
self.stylesheet['WarningBlock']
|
self.stylesheet['WarningBlock']
|
||||||
))
|
))
|
||||||
|
|
||||||
if self.invoice.custom_field:
|
if self.invoice.custom_field:
|
||||||
story.append(FontFallbackParagraph(
|
story.append(PlainTextParagraph(
|
||||||
'{}: {}'.format(
|
'{}: {}'.format(
|
||||||
self._clean_text(str(self.invoice.event.settings.invoice_address_custom_field)),
|
str(self.invoice.event.settings.invoice_address_custom_field),
|
||||||
self._clean_text(self.invoice.custom_field),
|
self.invoice.custom_field,
|
||||||
),
|
),
|
||||||
self.stylesheet['Normal']
|
self.stylesheet['Normal']
|
||||||
))
|
))
|
||||||
|
|
||||||
if self.invoice.internal_reference:
|
if self.invoice.internal_reference:
|
||||||
story.append(FontFallbackParagraph(
|
story.append(PlainTextParagraph(
|
||||||
self._normalize(pgettext('invoice', 'Customer reference: {reference}').format(
|
pgettext('invoice', 'Customer reference: {reference}').format(
|
||||||
reference=self._clean_text(self.invoice.internal_reference),
|
reference=self.invoice.internal_reference,
|
||||||
)),
|
),
|
||||||
self.stylesheet['Normal']
|
self.stylesheet['Normal']
|
||||||
))
|
))
|
||||||
|
|
||||||
if self.invoice.invoice_to_vat_id:
|
if self.invoice.invoice_to_vat_id:
|
||||||
story.append(FontFallbackParagraph(
|
story.append(PlainTextParagraph(
|
||||||
self._normalize(pgettext('invoice', 'Customer VAT ID')) + ': ' +
|
pgettext('invoice', 'Customer VAT ID') + ': ' + self.invoice.invoice_to_vat_id,
|
||||||
self._clean_text(self.invoice.invoice_to_vat_id),
|
|
||||||
self.stylesheet['Normal']
|
self.stylesheet['Normal']
|
||||||
))
|
))
|
||||||
|
|
||||||
if self.invoice.invoice_to_beneficiary:
|
if self.invoice.invoice_to_beneficiary:
|
||||||
story.append(FontFallbackParagraph(
|
story.append(PlainTextParagraph(
|
||||||
self._normalize(pgettext('invoice', 'Beneficiary')) + ':<br />' +
|
pgettext('invoice', 'Beneficiary') + ':\n' + self.invoice.invoice_to_beneficiary,
|
||||||
self._clean_text(self.invoice.invoice_to_beneficiary),
|
|
||||||
self.stylesheet['Normal']
|
self.stylesheet['Normal']
|
||||||
))
|
))
|
||||||
|
|
||||||
@@ -707,11 +696,11 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
|
|
||||||
story = [
|
story = [
|
||||||
NextPageTemplate('FirstPage'),
|
NextPageTemplate('FirstPage'),
|
||||||
FontFallbackParagraph(
|
PlainTextParagraph(
|
||||||
self._normalize(
|
(
|
||||||
pgettext('invoice', 'Tax Invoice') if str(self.invoice.invoice_from_country) == 'AU'
|
pgettext('invoice', 'Tax Invoice') if str(self.invoice.invoice_from_country) == 'AU'
|
||||||
else pgettext('invoice', 'Invoice')
|
else pgettext('invoice', 'Invoice')
|
||||||
) if not self.invoice.is_cancellation else self._normalize(pgettext('invoice', 'Cancellation')),
|
) if not self.invoice.is_cancellation else pgettext('invoice', 'Cancellation'),
|
||||||
self.stylesheet['Heading1']
|
self.stylesheet['Heading1']
|
||||||
),
|
),
|
||||||
Spacer(1, 5 * mm),
|
Spacer(1, 5 * mm),
|
||||||
@@ -733,17 +722,17 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
]
|
]
|
||||||
if has_taxes:
|
if has_taxes:
|
||||||
tdata = [(
|
tdata = [(
|
||||||
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Description')), self.stylesheet['Bold']),
|
PlainTextParagraph(pgettext('invoice', 'Description'), self.stylesheet['Bold']),
|
||||||
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Qty')), self.stylesheet['BoldRightNoSplit']),
|
PlainTextParagraph(pgettext('invoice', 'Qty'), self.stylesheet['BoldRightNoSplit']),
|
||||||
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Tax rate')), self.stylesheet['BoldRightNoSplit']),
|
PlainTextParagraph(pgettext('invoice', 'Tax rate'), self.stylesheet['BoldRightNoSplit']),
|
||||||
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Net')), self.stylesheet['BoldRightNoSplit']),
|
PlainTextParagraph(pgettext('invoice', 'Net'), self.stylesheet['BoldRightNoSplit']),
|
||||||
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Gross')), self.stylesheet['BoldRightNoSplit']),
|
PlainTextParagraph(pgettext('invoice', 'Gross'), self.stylesheet['BoldRightNoSplit']),
|
||||||
)]
|
)]
|
||||||
else:
|
else:
|
||||||
tdata = [(
|
tdata = [(
|
||||||
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Description')), self.stylesheet['Bold']),
|
PlainTextParagraph(pgettext('invoice', 'Description'), self.stylesheet['Bold']),
|
||||||
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Qty')), self.stylesheet['BoldRightNoSplit']),
|
PlainTextParagraph(pgettext('invoice', 'Qty'), self.stylesheet['BoldRightNoSplit']),
|
||||||
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Amount')), self.stylesheet['BoldRightNoSplit']),
|
PlainTextParagraph(pgettext('invoice', 'Amount'), self.stylesheet['BoldRightNoSplit']),
|
||||||
)]
|
)]
|
||||||
|
|
||||||
def _group_key(line):
|
def _group_key(line):
|
||||||
@@ -780,8 +769,8 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
max_height = self.stylesheet['Normal'].leading * 5
|
max_height = self.stylesheet['Normal'].leading * 5
|
||||||
p_style = self.stylesheet['Normal']
|
p_style = self.stylesheet['Normal']
|
||||||
for __ in range(1000):
|
for __ in range(1000):
|
||||||
p = FontFallbackParagraph(
|
p = PlainTextParagraph(
|
||||||
self._clean_text(curr_description, tags=['br']),
|
curr_description,
|
||||||
p_style
|
p_style
|
||||||
)
|
)
|
||||||
h = p.wrap(max_width, doc.height)[1]
|
h = p.wrap(max_width, doc.height)[1]
|
||||||
@@ -862,7 +851,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
# Group together at the end of the invoice
|
# Group together at the end of the invoice
|
||||||
request_show_service_date = period_line
|
request_show_service_date = period_line
|
||||||
elif period_line:
|
elif period_line:
|
||||||
description_p_list.append(FontFallbackParagraph(
|
description_p_list.append(PlainTextParagraph(
|
||||||
period_line,
|
period_line,
|
||||||
self.stylesheet['Fineprint']
|
self.stylesheet['Fineprint']
|
||||||
))
|
))
|
||||||
@@ -874,7 +863,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
net_price=money_filter(net_value, self.invoice.event.currency),
|
net_price=money_filter(net_value, self.invoice.event.currency),
|
||||||
gross_price=money_filter(gross_value, self.invoice.event.currency),
|
gross_price=money_filter(gross_value, self.invoice.event.currency),
|
||||||
)
|
)
|
||||||
description_p_list.append(FontFallbackParagraph(
|
description_p_list.append(PlainTextParagraph(
|
||||||
single_price_line,
|
single_price_line,
|
||||||
self.stylesheet['Fineprint']
|
self.stylesheet['Fineprint']
|
||||||
))
|
))
|
||||||
@@ -883,11 +872,11 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
description_p_list.pop(0),
|
description_p_list.pop(0),
|
||||||
str(len(lines)),
|
str(len(lines)),
|
||||||
localize(tax_rate) + " %",
|
localize(tax_rate) + " %",
|
||||||
FontFallbackParagraph(
|
PlainTextParagraph(
|
||||||
money_filter(net_value * len(lines), self.invoice.event.currency).replace('\xa0', ' '),
|
money_filter(net_value * len(lines), self.invoice.event.currency).replace('\xa0', ' '),
|
||||||
self.stylesheet['NormalRight']
|
self.stylesheet['NormalRight']
|
||||||
),
|
),
|
||||||
FontFallbackParagraph(
|
PlainTextParagraph(
|
||||||
money_filter(gross_value * len(lines), self.invoice.event.currency).replace('\xa0', ' '),
|
money_filter(gross_value * len(lines), self.invoice.event.currency).replace('\xa0', ' '),
|
||||||
self.stylesheet['NormalRight']
|
self.stylesheet['NormalRight']
|
||||||
),
|
),
|
||||||
@@ -904,14 +893,14 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
single_price_line = pgettext('invoice', 'Single price: {price}').format(
|
single_price_line = pgettext('invoice', 'Single price: {price}').format(
|
||||||
price=money_filter(gross_value, self.invoice.event.currency),
|
price=money_filter(gross_value, self.invoice.event.currency),
|
||||||
)
|
)
|
||||||
description_p_list.append(FontFallbackParagraph(
|
description_p_list.append(PlainTextParagraph(
|
||||||
single_price_line,
|
single_price_line,
|
||||||
self.stylesheet['Fineprint']
|
self.stylesheet['Fineprint']
|
||||||
))
|
))
|
||||||
tdata.append((
|
tdata.append((
|
||||||
description_p_list.pop(0),
|
description_p_list.pop(0),
|
||||||
str(len(lines)),
|
str(len(lines)),
|
||||||
FontFallbackParagraph(
|
PlainTextParagraph(
|
||||||
money_filter(gross_value * len(lines), self.invoice.event.currency).replace('\xa0', ' '),
|
money_filter(gross_value * len(lines), self.invoice.event.currency).replace('\xa0', ' '),
|
||||||
self.stylesheet['NormalRight']
|
self.stylesheet['NormalRight']
|
||||||
),
|
),
|
||||||
@@ -944,12 +933,12 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
|
|
||||||
if has_taxes:
|
if has_taxes:
|
||||||
tdata.append([
|
tdata.append([
|
||||||
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Invoice total')), self.stylesheet['Bold']), '', '', '',
|
PlainTextParagraph(pgettext('invoice', 'Invoice total'), self.stylesheet['Bold']), '', '', '',
|
||||||
money_filter(total, self.invoice.event.currency)
|
money_filter(total, self.invoice.event.currency)
|
||||||
])
|
])
|
||||||
else:
|
else:
|
||||||
tdata.append([
|
tdata.append([
|
||||||
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Invoice total')), self.stylesheet['Bold']), '',
|
PlainTextParagraph(pgettext('invoice', 'Invoice total'), self.stylesheet['Bold']), '',
|
||||||
money_filter(total, self.invoice.event.currency)
|
money_filter(total, self.invoice.event.currency)
|
||||||
])
|
])
|
||||||
|
|
||||||
@@ -958,12 +947,12 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
pending_sum = self.invoice.order.pending_sum
|
pending_sum = self.invoice.order.pending_sum
|
||||||
if pending_sum != total:
|
if pending_sum != total:
|
||||||
tdata.append(
|
tdata.append(
|
||||||
[FontFallbackParagraph(self._normalize(pgettext('invoice', 'Received payments')), self.stylesheet['Normal'])] +
|
[PlainTextParagraph(pgettext('invoice', 'Received payments'), self.stylesheet['Normal'])] +
|
||||||
(['', '', ''] if has_taxes else ['']) +
|
(['', '', ''] if has_taxes else ['']) +
|
||||||
[money_filter(pending_sum - total, self.invoice.event.currency)]
|
[money_filter(pending_sum - total, self.invoice.event.currency)]
|
||||||
)
|
)
|
||||||
tdata.append(
|
tdata.append(
|
||||||
[FontFallbackParagraph(self._normalize(pgettext('invoice', 'Outstanding payments')), self.stylesheet['Bold'])] +
|
[PlainTextParagraph(pgettext('invoice', 'Outstanding payments'), self.stylesheet['Bold'])] +
|
||||||
(['', '', ''] if has_taxes else ['']) +
|
(['', '', ''] if has_taxes else ['']) +
|
||||||
[money_filter(pending_sum, self.invoice.event.currency)]
|
[money_filter(pending_sum, self.invoice.event.currency)]
|
||||||
)
|
)
|
||||||
@@ -980,12 +969,12 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
s=Sum('amount')
|
s=Sum('amount')
|
||||||
)['s'] or Decimal('0.00')
|
)['s'] or Decimal('0.00')
|
||||||
tdata.append(
|
tdata.append(
|
||||||
[FontFallbackParagraph(self._normalize(pgettext('invoice', 'Paid by gift card')), self.stylesheet['Normal'])] +
|
[PlainTextParagraph(pgettext('invoice', 'Paid by gift card'), self.stylesheet['Normal'])] +
|
||||||
(['', '', ''] if has_taxes else ['']) +
|
(['', '', ''] if has_taxes else ['']) +
|
||||||
[money_filter(giftcard_sum, self.invoice.event.currency)]
|
[money_filter(giftcard_sum, self.invoice.event.currency)]
|
||||||
)
|
)
|
||||||
tdata.append(
|
tdata.append(
|
||||||
[FontFallbackParagraph(self._normalize(pgettext('invoice', 'Remaining amount')), self.stylesheet['Bold'])] +
|
[PlainTextParagraph(pgettext('invoice', 'Remaining amount'), self.stylesheet['Bold'])] +
|
||||||
(['', '', ''] if has_taxes else ['']) +
|
(['', '', ''] if has_taxes else ['']) +
|
||||||
[money_filter(total - giftcard_sum, self.invoice.event.currency)]
|
[money_filter(total - giftcard_sum, self.invoice.event.currency)]
|
||||||
)
|
)
|
||||||
@@ -1008,14 +997,14 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
story.append(Spacer(1, 10 * mm))
|
story.append(Spacer(1, 10 * mm))
|
||||||
|
|
||||||
if request_show_service_date:
|
if request_show_service_date:
|
||||||
story.append(FontFallbackParagraph(
|
story.append(PlainTextParagraph(
|
||||||
self._normalize(pgettext('invoice', 'Invoice period: {daterange}').format(daterange=request_show_service_date)),
|
pgettext('invoice', 'Invoice period: {daterange}').format(daterange=request_show_service_date),
|
||||||
self.stylesheet['Normal']
|
self.stylesheet['Normal']
|
||||||
))
|
))
|
||||||
|
|
||||||
if self.invoice.payment_provider_text:
|
if self.invoice.payment_provider_text:
|
||||||
story.append(FontFallbackParagraph(
|
story.append(FontFallbackParagraph(
|
||||||
self._normalize(self.invoice.payment_provider_text),
|
self._clean_text(self.invoice.payment_provider_text, tags=['br', 'b']),
|
||||||
self.stylesheet['Normal']
|
self.stylesheet['Normal']
|
||||||
))
|
))
|
||||||
|
|
||||||
@@ -1039,10 +1028,10 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
('FONTNAME', (0, 0), (-1, -1), self.font_regular),
|
('FONTNAME', (0, 0), (-1, -1), self.font_regular),
|
||||||
]
|
]
|
||||||
thead = [
|
thead = [
|
||||||
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Tax rate')), self.stylesheet['Fineprint']),
|
PlainTextParagraph(pgettext('invoice', 'Tax rate'), self.stylesheet['Fineprint']),
|
||||||
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Net value')), self.stylesheet['FineprintRight']),
|
PlainTextParagraph(pgettext('invoice', 'Net value'), self.stylesheet['FineprintRight']),
|
||||||
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Gross value')), self.stylesheet['FineprintRight']),
|
PlainTextParagraph(pgettext('invoice', 'Gross value'), self.stylesheet['FineprintRight']),
|
||||||
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Tax')), self.stylesheet['FineprintRight']),
|
PlainTextParagraph(pgettext('invoice', 'Tax'), self.stylesheet['FineprintRight']),
|
||||||
''
|
''
|
||||||
]
|
]
|
||||||
tdata = [thead]
|
tdata = [thead]
|
||||||
@@ -1053,7 +1042,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
continue
|
continue
|
||||||
tax = taxvalue_map[idx]
|
tax = taxvalue_map[idx]
|
||||||
tdata.append([
|
tdata.append([
|
||||||
FontFallbackParagraph(self._normalize(localize(rate) + " % " + name), self.stylesheet['Fineprint']),
|
PlainTextParagraph(localize(rate) + " % " + name, self.stylesheet['Fineprint']),
|
||||||
money_filter(gross - tax, self.invoice.event.currency),
|
money_filter(gross - tax, self.invoice.event.currency),
|
||||||
money_filter(gross, self.invoice.event.currency),
|
money_filter(gross, self.invoice.event.currency),
|
||||||
money_filter(tax, self.invoice.event.currency),
|
money_filter(tax, self.invoice.event.currency),
|
||||||
@@ -1072,7 +1061,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
table.setStyle(TableStyle(tstyledata))
|
table.setStyle(TableStyle(tstyledata))
|
||||||
story.append(Spacer(5 * mm, 5 * mm))
|
story.append(Spacer(5 * mm, 5 * mm))
|
||||||
story.append(KeepTogether([
|
story.append(KeepTogether([
|
||||||
FontFallbackParagraph(self._normalize(pgettext('invoice', 'Included taxes')), self.stylesheet['FineprintHeading']),
|
PlainTextParagraph(pgettext('invoice', 'Included taxes'), self.stylesheet['FineprintHeading']),
|
||||||
table
|
table
|
||||||
]))
|
]))
|
||||||
|
|
||||||
@@ -1089,7 +1078,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
net = gross - tax
|
net = gross - tax
|
||||||
|
|
||||||
tdata.append([
|
tdata.append([
|
||||||
FontFallbackParagraph(self._normalize(localize(rate) + " % " + name), self.stylesheet['Fineprint']),
|
PlainTextParagraph(localize(rate) + " % " + name, self.stylesheet['Fineprint']),
|
||||||
fmt(net), fmt(gross), fmt(tax), ''
|
fmt(net), fmt(gross), fmt(tax), ''
|
||||||
])
|
])
|
||||||
|
|
||||||
@@ -1098,13 +1087,13 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
|
|
||||||
story.append(KeepTogether([
|
story.append(KeepTogether([
|
||||||
Spacer(1, height=2 * mm),
|
Spacer(1, height=2 * mm),
|
||||||
FontFallbackParagraph(
|
PlainTextParagraph(
|
||||||
self._normalize(pgettext(
|
pgettext(
|
||||||
'invoice', 'Using the conversion rate of 1:{rate} as published by the {authority} on '
|
'invoice', 'Using the conversion rate of 1:{rate} as published by the {authority} on '
|
||||||
'{date}, this corresponds to:'
|
'{date}, this corresponds to:'
|
||||||
).format(rate=localize(self.invoice.foreign_currency_rate),
|
).format(rate=localize(self.invoice.foreign_currency_rate),
|
||||||
authority=SOURCE_NAMES.get(self.invoice.foreign_currency_source, "?"),
|
authority=SOURCE_NAMES.get(self.invoice.foreign_currency_source, "?"),
|
||||||
date=date_format(self.invoice.foreign_currency_rate_date, "SHORT_DATE_FORMAT"))),
|
date=date_format(self.invoice.foreign_currency_rate_date, "SHORT_DATE_FORMAT")),
|
||||||
self.stylesheet['Fineprint']
|
self.stylesheet['Fineprint']
|
||||||
),
|
),
|
||||||
Spacer(1, height=3 * mm),
|
Spacer(1, height=3 * mm),
|
||||||
@@ -1113,14 +1102,14 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
|||||||
elif self.invoice.foreign_currency_display and self.invoice.foreign_currency_rate:
|
elif self.invoice.foreign_currency_display and self.invoice.foreign_currency_rate:
|
||||||
foreign_total = round_decimal(total * self.invoice.foreign_currency_rate)
|
foreign_total = round_decimal(total * self.invoice.foreign_currency_rate)
|
||||||
story.append(Spacer(1, 5 * mm))
|
story.append(Spacer(1, 5 * mm))
|
||||||
story.append(FontFallbackParagraph(self._normalize(
|
story.append(PlainTextParagraph(
|
||||||
pgettext(
|
pgettext(
|
||||||
'invoice', 'Using the conversion rate of 1:{rate} as published by the {authority} on '
|
'invoice', 'Using the conversion rate of 1:{rate} as published by the {authority} on '
|
||||||
'{date}, the invoice total corresponds to {total}.'
|
'{date}, the invoice total corresponds to {total}.'
|
||||||
).format(rate=localize(self.invoice.foreign_currency_rate),
|
).format(rate=localize(self.invoice.foreign_currency_rate),
|
||||||
date=date_format(self.invoice.foreign_currency_rate_date, "SHORT_DATE_FORMAT"),
|
date=date_format(self.invoice.foreign_currency_rate_date, "SHORT_DATE_FORMAT"),
|
||||||
authority=SOURCE_NAMES.get(self.invoice.foreign_currency_source, "?"),
|
authority=SOURCE_NAMES.get(self.invoice.foreign_currency_source, "?"),
|
||||||
total=fmt(foreign_total))),
|
total=fmt(foreign_total)),
|
||||||
self.stylesheet['Fineprint']
|
self.stylesheet['Fineprint']
|
||||||
))
|
))
|
||||||
|
|
||||||
@@ -1162,11 +1151,8 @@ class Modern1Renderer(ClassicInvoiceRenderer):
|
|||||||
def _draw_invoice_from(self, canvas):
|
def _draw_invoice_from(self, canvas):
|
||||||
if not self.invoice.address_invoice_from:
|
if not self.invoice.address_invoice_from:
|
||||||
return
|
return
|
||||||
c = [
|
c = self.invoice.address_invoice_from.strip().split('\n')
|
||||||
self._clean_text(l)
|
p = PlainTextParagraph(' · '.join(c), style=self.stylesheet['Sender'])
|
||||||
for l in self.invoice.address_invoice_from.strip().split('\n')
|
|
||||||
]
|
|
||||||
p = FontFallbackParagraph(self._normalize(' · '.join(c)), style=self.stylesheet['Sender'])
|
|
||||||
p.wrapOn(canvas, self.invoice_to_width, 15.7 * mm)
|
p.wrapOn(canvas, self.invoice_to_width, 15.7 * mm)
|
||||||
p.drawOn(canvas, self.invoice_to_left, self.pagesize[1] - self.invoice_to_top + 2 * mm)
|
p.drawOn(canvas, self.invoice_to_left, self.pagesize[1] - self.invoice_to_top + 2 * mm)
|
||||||
super()._draw_invoice_from(canvas)
|
super()._draw_invoice_from(canvas)
|
||||||
@@ -1225,8 +1211,8 @@ class Modern1Renderer(ClassicInvoiceRenderer):
|
|||||||
_draw(pgettext('invoice', 'Order code'), self.invoice.order.full_code, value_size, self.left_margin, 45 * mm, **kwargs)
|
_draw(pgettext('invoice', 'Order code'), self.invoice.order.full_code, value_size, self.left_margin, 45 * mm, **kwargs)
|
||||||
]
|
]
|
||||||
|
|
||||||
p = FontFallbackParagraph(
|
p = PlainTextParagraph(
|
||||||
self._normalize(date_format(self.invoice.date, "DATE_FORMAT")),
|
date_format(self.invoice.date, "DATE_FORMAT"),
|
||||||
style=ParagraphStyle(name=f'Normal{value_size}', fontName=self.font_regular, fontSize=value_size, leading=value_size * 1.2)
|
style=ParagraphStyle(name=f'Normal{value_size}', fontName=self.font_regular, fontSize=value_size, leading=value_size * 1.2)
|
||||||
)
|
)
|
||||||
w = stringWidth(p.text, p.frags[0].fontName, p.frags[0].fontSize)
|
w = stringWidth(p.text, p.frags[0].fontName, p.frags[0].fontSize)
|
||||||
@@ -1283,7 +1269,7 @@ class Modern1SimplifiedRenderer(Modern1Renderer):
|
|||||||
i = []
|
i = []
|
||||||
|
|
||||||
if not self.invoice.event.has_subevents and self.invoice.event.settings.show_dates_on_frontpage:
|
if not self.invoice.event.has_subevents and self.invoice.event.settings.show_dates_on_frontpage:
|
||||||
i.append(FontFallbackParagraph(
|
i.append(PlainTextParagraph(
|
||||||
pgettext('invoice', 'Event date: {date_range}').format(
|
pgettext('invoice', 'Event date: {date_range}').format(
|
||||||
date_range=self.invoice.event.get_date_range_display(),
|
date_range=self.invoice.event.get_date_range_display(),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ class Command(Parent):
|
|||||||
# Start the vite server in the background
|
# Start the vite server in the background
|
||||||
vite_server = subprocess.Popen(
|
vite_server = subprocess.Popen(
|
||||||
["npm", "run", "dev:control"],
|
["npm", "run", "dev:control"],
|
||||||
cwd=Path(__file__).parent.parent.parent.parent.parent
|
cwd=Path(__file__).parent.parent.parent.parent.parent,
|
||||||
|
stdin=subprocess.DEVNULL
|
||||||
)
|
)
|
||||||
|
|
||||||
def cleanup():
|
def cleanup():
|
||||||
|
|||||||
+20
-14
@@ -26,6 +26,7 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
|
|
||||||
class BaseMediaType:
|
class BaseMediaType:
|
||||||
medium_created_by_server = False
|
medium_created_by_server = False
|
||||||
|
medium_created_from_unknown_supported = False
|
||||||
supports_orderposition = False
|
supports_orderposition = False
|
||||||
supports_giftcard = False
|
supports_giftcard = False
|
||||||
|
|
||||||
@@ -56,7 +57,7 @@ class BaseMediaType:
|
|||||||
def is_active(self, organizer):
|
def is_active(self, organizer):
|
||||||
return organizer.settings.get(f'reusable_media_type_{self.identifier}', as_type=bool, default=False)
|
return organizer.settings.get(f'reusable_media_type_{self.identifier}', as_type=bool, default=False)
|
||||||
|
|
||||||
def handle_unknown(self, organizer, identifier, user, auth):
|
def handle_unknown(self, organizer, identifier, user, auth, force_create=False):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def handle_new(self, organizer, medium, user, auth):
|
def handle_new(self, organizer, medium, user, auth):
|
||||||
@@ -88,23 +89,32 @@ class NfcUidMediaType(BaseMediaType):
|
|||||||
verbose_name = _('NFC UID-based')
|
verbose_name = _('NFC UID-based')
|
||||||
icon = 'pretixbase/img/media/nfc_uid.svg'
|
icon = 'pretixbase/img/media/nfc_uid.svg'
|
||||||
medium_created_by_server = False
|
medium_created_by_server = False
|
||||||
|
medium_created_from_unknown_supported = True
|
||||||
supports_giftcard = True
|
supports_giftcard = True
|
||||||
supports_orderposition = False
|
supports_orderposition = True
|
||||||
|
|
||||||
def handle_unknown(self, organizer, identifier, user, auth):
|
def handle_unknown(self, organizer, identifier, user, auth, force_create=False):
|
||||||
from pretix.base.models import GiftCard, ReusableMedium
|
from pretix.base.models import GiftCard, ReusableMedium
|
||||||
|
|
||||||
if organizer.settings.get(f'reusable_media_type_{self.identifier}_autocreate_giftcard', as_type=bool):
|
create_giftcard = organizer.settings.get(f'reusable_media_type_{self.identifier}_autocreate_giftcard', as_type=bool)
|
||||||
|
if create_giftcard or force_create:
|
||||||
if identifier.startswith("08"):
|
if identifier.startswith("08"):
|
||||||
# Don't create gift cards for NFC UIDs that start with 08, which represents NFC cards that issue random
|
# Don't create gift cards for NFC UIDs that start with 08, which represents NFC cards that issue random
|
||||||
# UIDs on every read, so they won't be useful.
|
# UIDs on every read, so they won't be useful.
|
||||||
return
|
return
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
gc = GiftCard.objects.create(
|
if create_giftcard:
|
||||||
issuer=organizer,
|
gc = GiftCard.objects.create(
|
||||||
expires=organizer.default_gift_card_expiry,
|
issuer=organizer,
|
||||||
currency=organizer.settings.get(f'reusable_media_type_{self.identifier}_autocreate_giftcard_currency'),
|
expires=organizer.default_gift_card_expiry,
|
||||||
)
|
currency=organizer.settings.get(f'reusable_media_type_{self.identifier}_autocreate_giftcard_currency'),
|
||||||
|
)
|
||||||
|
gc.log_action(
|
||||||
|
'pretix.giftcards.created',
|
||||||
|
user=user, auth=auth,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
gc = None
|
||||||
m = ReusableMedium.objects.create(
|
m = ReusableMedium.objects.create(
|
||||||
type=self.identifier,
|
type=self.identifier,
|
||||||
identifier=identifier,
|
identifier=identifier,
|
||||||
@@ -116,10 +126,6 @@ class NfcUidMediaType(BaseMediaType):
|
|||||||
'pretix.reusable_medium.created.auto',
|
'pretix.reusable_medium.created.auto',
|
||||||
user=user, auth=auth,
|
user=user, auth=auth,
|
||||||
)
|
)
|
||||||
gc.log_action(
|
|
||||||
'pretix.giftcards.created',
|
|
||||||
user=user, auth=auth,
|
|
||||||
)
|
|
||||||
return m
|
return m
|
||||||
|
|
||||||
|
|
||||||
@@ -129,7 +135,7 @@ class NfcMf0aesMediaType(BaseMediaType):
|
|||||||
icon = 'pretixbase/img/media/nfc_secure.svg'
|
icon = 'pretixbase/img/media/nfc_secure.svg'
|
||||||
medium_created_by_server = False
|
medium_created_by_server = False
|
||||||
supports_giftcard = True
|
supports_giftcard = True
|
||||||
supports_orderposition = False
|
supports_orderposition = True
|
||||||
|
|
||||||
def handle_new(self, organizer, medium, user, auth):
|
def handle_new(self, organizer, medium, user, auth):
|
||||||
from pretix.base.models import GiftCard
|
from pretix.base.models import GiftCard
|
||||||
|
|||||||
@@ -282,10 +282,12 @@ def metric_values():
|
|||||||
|
|
||||||
# Throwaway metrics
|
# Throwaway metrics
|
||||||
exact_tables = [
|
exact_tables = [
|
||||||
Order, OrderPosition, Invoice, Event, Organizer
|
Order, Invoice, Event, Organizer
|
||||||
]
|
]
|
||||||
for m in apps.get_models(): # Count all models
|
for m in apps.get_models(): # Count all models
|
||||||
if any(issubclass(m, p) for p in exact_tables):
|
if issubclass(m, OrderPosition):
|
||||||
|
metrics['pretix_model_instances']['{model="%s"}' % m._meta] = m.all.count()
|
||||||
|
elif any(issubclass(m, p) for p in exact_tables):
|
||||||
metrics['pretix_model_instances']['{model="%s"}' % m._meta] = m.objects.count()
|
metrics['pretix_model_instances']['{model="%s"}' % m._meta] = m.objects.count()
|
||||||
else:
|
else:
|
||||||
metrics['pretix_model_instances']['{model="%s"}' % m._meta] = estimate_count_fast(m)
|
metrics['pretix_model_instances']['{model="%s"}' % m._meta] = estimate_count_fast(m)
|
||||||
|
|||||||
@@ -19,6 +19,8 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||||
# <https://www.gnu.org/licenses/>.
|
# <https://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from urllib.parse import urlparse, urlsplit
|
from urllib.parse import urlparse, urlsplit
|
||||||
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
|
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
|
||||||
@@ -43,6 +45,8 @@ from pretix.multidomain.urlreverse import (
|
|||||||
)
|
)
|
||||||
from pretix.presale.style import get_fonts
|
from pretix.presale.style import get_fonts
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
_supported = None
|
_supported = None
|
||||||
|
|
||||||
|
|
||||||
@@ -74,6 +78,7 @@ class LocaleMiddleware(MiddlewareMixin):
|
|||||||
|
|
||||||
def process_request(self, request: HttpRequest):
|
def process_request(self, request: HttpRequest):
|
||||||
language = get_language_from_request(request)
|
language = get_language_from_request(request)
|
||||||
|
region = None
|
||||||
# Normally, this middleware runs *before* the event is set. However, on event frontend pages it
|
# Normally, this middleware runs *before* the event is set. However, on event frontend pages it
|
||||||
# might be run a second time by pretix.presale.EventMiddleware and in this case the event is already
|
# might be run a second time by pretix.presale.EventMiddleware and in this case the event is already
|
||||||
# set and can be taken into account for the decision.
|
# set and can be taken into account for the decision.
|
||||||
@@ -94,15 +99,16 @@ class LocaleMiddleware(MiddlewareMixin):
|
|||||||
if '-' not in language and settings_holder.settings.region:
|
if '-' not in language and settings_holder.settings.region:
|
||||||
language += '-' + settings_holder.settings.region
|
language += '-' + settings_holder.settings.region
|
||||||
if settings_holder.settings.region:
|
if settings_holder.settings.region:
|
||||||
set_region(settings_holder.settings.region)
|
region = settings_holder.settings.region
|
||||||
else:
|
else:
|
||||||
gs = global_settings_object(request)
|
gs = global_settings_object(request)
|
||||||
if '-' not in language and gs.settings.region:
|
if '-' not in language and gs.settings.region:
|
||||||
language += '-' + gs.settings.region
|
language += '-' + gs.settings.region
|
||||||
if gs.settings.region:
|
if gs.settings.region:
|
||||||
set_region(gs.settings.region)
|
region = gs.settings.region
|
||||||
|
|
||||||
translation.activate(language)
|
translation.activate(language)
|
||||||
|
set_region(region)
|
||||||
request.LANGUAGE_CODE = get_language_without_region()
|
request.LANGUAGE_CODE = get_language_without_region()
|
||||||
|
|
||||||
tzname = None
|
tzname = None
|
||||||
@@ -221,7 +227,26 @@ def _parse_csp(header):
|
|||||||
return h
|
return h
|
||||||
|
|
||||||
|
|
||||||
|
VALID_CSP_DIRECTIVES = [
|
||||||
|
"child-src", "connect-src", "default-src", "fenced-frame-src", "font-src", "form-action", "frame-src", "img-src",
|
||||||
|
"manifest-src", "media-src", "object-src", "prefetch-src", "report-uri", "script-src", "script-src-elem",
|
||||||
|
"script-src-attr", "style-src", "style-src-elem", "style-src-attr", "worker-src",
|
||||||
|
]
|
||||||
|
|
||||||
|
CSP_ILLEGAL_CHARS = re.compile(r'[\s,;]')
|
||||||
|
|
||||||
|
|
||||||
|
def _sanitize_csp(h):
|
||||||
|
for k, v in h.items():
|
||||||
|
if k not in VALID_CSP_DIRECTIVES:
|
||||||
|
raise ValueError("Invalid CSP directive " + k)
|
||||||
|
if any(CSP_ILLEGAL_CHARS.search(el) for el in v):
|
||||||
|
logger.warning("Stripping invalid component from CSP: %r", h)
|
||||||
|
h[k] = [el for el in v if not CSP_ILLEGAL_CHARS.search(el)]
|
||||||
|
|
||||||
|
|
||||||
def _render_csp(h):
|
def _render_csp(h):
|
||||||
|
_sanitize_csp(h)
|
||||||
return "; ".join(k + ' ' + ' '.join(v) for k, v in h.items() if v)
|
return "; ".join(k + ' ' + ' '.join(v) for k, v in h.items() if v)
|
||||||
|
|
||||||
|
|
||||||
@@ -241,21 +266,7 @@ def _merge_csp(a, b):
|
|||||||
|
|
||||||
|
|
||||||
class SecurityMiddleware(MiddlewareMixin):
|
class SecurityMiddleware(MiddlewareMixin):
|
||||||
CSP_EXEMPT = (
|
|
||||||
'/api/v1/docs/',
|
|
||||||
)
|
|
||||||
|
|
||||||
def process_response(self, request, resp):
|
def process_response(self, request, resp):
|
||||||
def nested_dict_values(d):
|
|
||||||
for v in d.values():
|
|
||||||
if isinstance(v, dict):
|
|
||||||
yield from nested_dict_values(v)
|
|
||||||
else:
|
|
||||||
if isinstance(v, str):
|
|
||||||
yield v
|
|
||||||
|
|
||||||
url = resolve(request.path_info)
|
|
||||||
|
|
||||||
if settings.DEBUG and resp.status_code >= 400:
|
if settings.DEBUG and resp.status_code >= 400:
|
||||||
# Don't use CSP on debug error page as it breaks of Django's fancy error
|
# Don't use CSP on debug error page as it breaks of Django's fancy error
|
||||||
# pages
|
# pages
|
||||||
@@ -266,18 +277,15 @@ class SecurityMiddleware(MiddlewareMixin):
|
|||||||
# https://github.com/pretix/pretix/issues/765
|
# https://github.com/pretix/pretix/issues/765
|
||||||
resp['P3P'] = 'CP=\"ALL DSP COR CUR ADM TAI OUR IND COM NAV INT\"'
|
resp['P3P'] = 'CP=\"ALL DSP COR CUR ADM TAI OUR IND COM NAV INT\"'
|
||||||
|
|
||||||
img_src = []
|
if not getattr(resp, '_csp_ignore', False):
|
||||||
gs = global_settings_object(request)
|
resp['Content-Security-Policy'] = _render_csp(self._build_csp(request, resp))
|
||||||
if gs.settings.leaflet_tiles:
|
elif 'Content-Security-Policy' in resp:
|
||||||
img_src.append(gs.settings.leaflet_tiles[:gs.settings.leaflet_tiles.index("/", 10)].replace("{s}", "*"))
|
del resp['Content-Security-Policy']
|
||||||
|
|
||||||
font_src = set()
|
return resp
|
||||||
if hasattr(request, 'event'):
|
|
||||||
for font in get_fonts(request.event, pdf_support_required=False).values():
|
def _build_csp(self, request, resp):
|
||||||
for path in list(nested_dict_values(font)):
|
url = resolve(request.path_info)
|
||||||
font_location = urlparse(path)
|
|
||||||
if font_location.scheme and font_location.netloc:
|
|
||||||
font_src.add('{}://{}'.format(font_location.scheme, font_location.netloc))
|
|
||||||
|
|
||||||
h = {
|
h = {
|
||||||
'default-src': ["{static}"],
|
'default-src': ["{static}"],
|
||||||
@@ -286,8 +294,8 @@ class SecurityMiddleware(MiddlewareMixin):
|
|||||||
'frame-src': ['{static}'],
|
'frame-src': ['{static}'],
|
||||||
'style-src': ["{static}", "{media}"],
|
'style-src': ["{static}", "{media}"],
|
||||||
'connect-src': ["{dynamic}", "{media}"],
|
'connect-src': ["{dynamic}", "{media}"],
|
||||||
'img-src': ["{static}", "{media}", "data:"] + img_src,
|
'img-src': ["{static}", "{media}", "data:"],
|
||||||
'font-src': ["{static}"] + list(font_src),
|
'font-src': ["{static}"],
|
||||||
'media-src': ["{static}", "data:"],
|
'media-src': ["{static}", "data:"],
|
||||||
# form-action is not only used to match on form actions, but also on URLs
|
# form-action is not only used to match on form actions, but also on URLs
|
||||||
# form-actions redirect to. In the context of e.g. payment providers or
|
# form-actions redirect to. In the context of e.g. payment providers or
|
||||||
@@ -296,6 +304,13 @@ class SecurityMiddleware(MiddlewareMixin):
|
|||||||
'form-action': ["{dynamic}", "https:"] + (['http:'] if settings.SITE_URL.startswith('http://') else []),
|
'form-action': ["{dynamic}", "https:"] + (['http:'] if settings.SITE_URL.startswith('http://') else []),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gs = global_settings_object(request)
|
||||||
|
if gs.settings.leaflet_tiles:
|
||||||
|
h['img-src'].append(gs.settings.leaflet_tiles[:gs.settings.leaflet_tiles.index("/", 10)].replace("{s}", "*"))
|
||||||
|
|
||||||
|
if hasattr(request, 'event'):
|
||||||
|
h['font-src'] += list(self._get_font_origins(request.event))
|
||||||
|
|
||||||
if settings.VITE_DEV_MODE:
|
if settings.VITE_DEV_MODE:
|
||||||
h['script-src'] += ["http://localhost:5173", "ws://localhost:5173"]
|
h['script-src'] += ["http://localhost:5173", "ws://localhost:5173"]
|
||||||
h['style-src'] += ["'unsafe-inline'"]
|
h['style-src'] += ["'unsafe-inline'"]
|
||||||
@@ -307,6 +322,7 @@ class SecurityMiddleware(MiddlewareMixin):
|
|||||||
if not settings.VITE_DEV_MODE:
|
if not settings.VITE_DEV_MODE:
|
||||||
# can't have 'unsafe-inline' and nonce at the same time
|
# can't have 'unsafe-inline' and nonce at the same time
|
||||||
h['style-src'].append(nonce)
|
h['style-src'].append(nonce)
|
||||||
|
|
||||||
# Only include pay.google.com for wallet detection purposes on the Payment selection page
|
# Only include pay.google.com for wallet detection purposes on the Payment selection page
|
||||||
if (
|
if (
|
||||||
url.url_name == "event.order.pay.change" or
|
url.url_name == "event.order.pay.change" or
|
||||||
@@ -315,27 +331,32 @@ class SecurityMiddleware(MiddlewareMixin):
|
|||||||
h['script-src'].append('https://pay.google.com')
|
h['script-src'].append('https://pay.google.com')
|
||||||
h['frame-src'].append('https://pay.google.com')
|
h['frame-src'].append('https://pay.google.com')
|
||||||
h['connect-src'].append('https://google.com/pay')
|
h['connect-src'].append('https://google.com/pay')
|
||||||
|
|
||||||
if settings.LOG_CSP:
|
if settings.LOG_CSP:
|
||||||
h['report-uri'] = ["/csp_report/"]
|
h['report-uri'] = ["/csp_report/"]
|
||||||
|
|
||||||
if 'Content-Security-Policy' in resp:
|
if 'Content-Security-Policy' in resp:
|
||||||
_merge_csp(h, _parse_csp(resp['Content-Security-Policy']))
|
_merge_csp(h, _parse_csp(resp['Content-Security-Policy']))
|
||||||
|
|
||||||
if settings.CSP_ADDITIONAL_HEADER:
|
if settings.CSP_ADDITIONAL_HEADER:
|
||||||
_merge_csp(h, _parse_csp(settings.CSP_ADDITIONAL_HEADER))
|
_merge_csp(h, _parse_csp(settings.CSP_ADDITIONAL_HEADER))
|
||||||
|
|
||||||
staticdomain = "'self'"
|
placeholders = {
|
||||||
dynamicdomain = "'self'"
|
"{static}": ["'self'"],
|
||||||
mediadomain = "'self'"
|
"{dynamic}": ["'self'"],
|
||||||
|
"{media}": ["'self'"],
|
||||||
|
}
|
||||||
if settings.MEDIA_URL.startswith('http'):
|
if settings.MEDIA_URL.startswith('http'):
|
||||||
mediadomain += " " + settings.MEDIA_URL[:settings.MEDIA_URL.find('/', 9)]
|
placeholders["{media}"].append(settings.MEDIA_URL[:settings.MEDIA_URL.find('/', 9)])
|
||||||
if settings.STATIC_URL.startswith('http'):
|
if settings.STATIC_URL.startswith('http'):
|
||||||
staticdomain += " " + settings.STATIC_URL[:settings.STATIC_URL.find('/', 9)]
|
placeholders["{static}"].append(settings.STATIC_URL[:settings.STATIC_URL.find('/', 9)])
|
||||||
if settings.SITE_URL.startswith('http'):
|
if settings.SITE_URL.startswith('http'):
|
||||||
if settings.SITE_URL.find('/', 9) > 0:
|
if settings.SITE_URL.find('/', 9) > 0:
|
||||||
staticdomain += " " + settings.SITE_URL[:settings.SITE_URL.find('/', 9)]
|
placeholders["{static}"].append(settings.SITE_URL[:settings.SITE_URL.find('/', 9)])
|
||||||
dynamicdomain += " " + settings.SITE_URL[:settings.SITE_URL.find('/', 9)]
|
placeholders["{dynamic}"].append(settings.SITE_URL[:settings.SITE_URL.find('/', 9)])
|
||||||
else:
|
else:
|
||||||
staticdomain += " " + settings.SITE_URL
|
placeholders["{static}"].append(settings.SITE_URL)
|
||||||
dynamicdomain += " " + settings.SITE_URL
|
placeholders["{dynamic}"].append(settings.SITE_URL)
|
||||||
|
|
||||||
if hasattr(request, 'organizer') and request.organizer:
|
if hasattr(request, 'organizer') and request.organizer:
|
||||||
if hasattr(request, 'event') and request.event:
|
if hasattr(request, 'event') and request.event:
|
||||||
@@ -346,18 +367,29 @@ class SecurityMiddleware(MiddlewareMixin):
|
|||||||
siteurlsplit = urlsplit(settings.SITE_URL)
|
siteurlsplit = urlsplit(settings.SITE_URL)
|
||||||
if siteurlsplit.port and siteurlsplit.port not in (80, 443):
|
if siteurlsplit.port and siteurlsplit.port not in (80, 443):
|
||||||
domain = '%s:%d' % (domain, siteurlsplit.port)
|
domain = '%s:%d' % (domain, siteurlsplit.port)
|
||||||
dynamicdomain += " " + domain
|
placeholders["{dynamic}"].append(domain)
|
||||||
|
|
||||||
if request.path not in self.CSP_EXEMPT and not getattr(resp, '_csp_ignore', False):
|
for k, v in h.items():
|
||||||
resp['Content-Security-Policy'] = _render_csp(h).format(static=staticdomain, dynamic=dynamicdomain,
|
h[k] = sorted(set(result for part in v for result in placeholders.get(part, [part])))
|
||||||
media=mediadomain)
|
|
||||||
for k, v in h.items():
|
|
||||||
h[k] = sorted(set(' '.join(v).format(static=staticdomain, dynamic=dynamicdomain, media=mediadomain).split(' ')))
|
|
||||||
resp['Content-Security-Policy'] = _render_csp(h)
|
|
||||||
elif 'Content-Security-Policy' in resp:
|
|
||||||
del resp['Content-Security-Policy']
|
|
||||||
|
|
||||||
return resp
|
return h
|
||||||
|
|
||||||
|
def _get_font_origins(self, event):
|
||||||
|
def nested_dict_values(d):
|
||||||
|
for v in d.values():
|
||||||
|
if isinstance(v, dict):
|
||||||
|
yield from nested_dict_values(v)
|
||||||
|
else:
|
||||||
|
if isinstance(v, str):
|
||||||
|
yield v
|
||||||
|
|
||||||
|
font_src = set()
|
||||||
|
for font in get_fonts(event, pdf_support_required=False).values():
|
||||||
|
for path in list(nested_dict_values(font)):
|
||||||
|
font_location = urlparse(path)
|
||||||
|
if font_location.scheme and font_location.netloc:
|
||||||
|
font_src.add('{}://{}'.format(font_location.scheme, font_location.netloc))
|
||||||
|
return font_src
|
||||||
|
|
||||||
|
|
||||||
class RejectInvalidInputMiddleware(MiddlewareMixin):
|
class RejectInvalidInputMiddleware(MiddlewareMixin):
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ from django_otp.models import Device
|
|||||||
from django_scopes import scopes_disabled
|
from django_scopes import scopes_disabled
|
||||||
|
|
||||||
from pretix.base.i18n import language
|
from pretix.base.i18n import language
|
||||||
from pretix.helpers.urls import build_absolute_uri
|
from pretix.helpers.urls import mainreverse_absolute
|
||||||
|
|
||||||
from ...helpers.countries import FastCountryField
|
from ...helpers.countries import FastCountryField
|
||||||
from ...helpers.u2f import pub_key_from_der, websafe_decode
|
from ...helpers.u2f import pub_key_from_der, websafe_decode
|
||||||
@@ -378,7 +378,7 @@ class User(AbstractBaseUser, PermissionsMixin, LoggingMixin):
|
|||||||
{
|
{
|
||||||
'user': self,
|
'user': self,
|
||||||
'messages': msg,
|
'messages': msg,
|
||||||
'url': build_absolute_uri('control:user.settings'),
|
'url': mainreverse_absolute('control:user.settings'),
|
||||||
'instance': settings.PRETIX_INSTANCE_NAME,
|
'instance': settings.PRETIX_INSTANCE_NAME,
|
||||||
},
|
},
|
||||||
event=None,
|
event=None,
|
||||||
@@ -466,7 +466,7 @@ class User(AbstractBaseUser, PermissionsMixin, LoggingMixin):
|
|||||||
{
|
{
|
||||||
'instance': settings.PRETIX_INSTANCE_NAME,
|
'instance': settings.PRETIX_INSTANCE_NAME,
|
||||||
'user': self,
|
'user': self,
|
||||||
'url': (build_absolute_uri('control:auth.forgot.recover')
|
'url': (mainreverse_absolute('control:auth.forgot.recover')
|
||||||
+ '?id=%d&token=%s' % (self.id, default_token_generator.make_token(self)))
|
+ '?id=%d&token=%s' % (self.id, default_token_generator.make_token(self)))
|
||||||
},
|
},
|
||||||
None, locale=self.locale, user=self
|
None, locale=self.locale, user=self
|
||||||
@@ -647,25 +647,22 @@ class User(AbstractBaseUser, PermissionsMixin, LoggingMixin):
|
|||||||
id__in=self.teams.filter(TeamQuerySet.organizer_permission_q(permission)).values_list('organizer', flat=True)
|
id__in=self.teams.filter(TeamQuerySet.organizer_permission_q(permission)).values_list('organizer', flat=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
def has_active_staff_session(self, session_key=None):
|
def has_active_staff_session(self, session_key):
|
||||||
"""
|
"""
|
||||||
Returns whether or not a user has an active staff session (formerly known as superuser session)
|
Returns whether or not a user has an active staff session (formerly known as superuser session)
|
||||||
with the given session key.
|
with the given session key.
|
||||||
"""
|
"""
|
||||||
return self.get_active_staff_session(session_key) is not None
|
return self.get_active_staff_session(session_key) is not None
|
||||||
|
|
||||||
def get_active_staff_session(self, session_key=None):
|
def get_active_staff_session(self, session_key):
|
||||||
if not self.is_staff:
|
if not self.is_staff or not session_key:
|
||||||
return None
|
return None
|
||||||
if not hasattr(self, '_staff_session_cache'):
|
if not hasattr(self, '_staff_session_cache'):
|
||||||
self._staff_session_cache = {}
|
self._staff_session_cache = {}
|
||||||
if session_key not in self._staff_session_cache:
|
if session_key not in self._staff_session_cache:
|
||||||
qs = StaffSession.objects.filter(
|
sess = StaffSession.objects.filter(
|
||||||
user=self, date_end__isnull=True
|
user=self, date_end__isnull=True, session_key=session_key
|
||||||
)
|
).first()
|
||||||
if session_key:
|
|
||||||
qs = qs.filter(session_key=session_key)
|
|
||||||
sess = qs.first()
|
|
||||||
if sess:
|
if sess:
|
||||||
if sess.date_start < now() - timedelta(seconds=settings.PRETIX_SESSION_TIMEOUT_ABSOLUTE):
|
if sess.date_start < now() - timedelta(seconds=settings.PRETIX_SESSION_TIMEOUT_ABSOLUTE):
|
||||||
sess.date_end = now()
|
sess.date_end = now()
|
||||||
|
|||||||
@@ -346,11 +346,14 @@ class Checkin(models.Model):
|
|||||||
REASON_INCOMPLETE = 'incomplete'
|
REASON_INCOMPLETE = 'incomplete'
|
||||||
REASON_ALREADY_REDEEMED = 'already_redeemed'
|
REASON_ALREADY_REDEEMED = 'already_redeemed'
|
||||||
REASON_AMBIGUOUS = 'ambiguous'
|
REASON_AMBIGUOUS = 'ambiguous'
|
||||||
|
REASON_MEDIUM_INVALID = 'medium_invalid'
|
||||||
|
REASON_MEDIUM_EXISTS = 'medium_exists'
|
||||||
REASON_ERROR = 'error'
|
REASON_ERROR = 'error'
|
||||||
REASON_BLOCKED = 'blocked'
|
REASON_BLOCKED = 'blocked'
|
||||||
REASON_UNAPPROVED = 'unapproved'
|
REASON_UNAPPROVED = 'unapproved'
|
||||||
REASON_INVALID_TIME = 'invalid_time'
|
REASON_INVALID_TIME = 'invalid_time'
|
||||||
REASON_ANNULLED = 'annulled'
|
REASON_ANNULLED = 'annulled'
|
||||||
|
REASON_ALREADY_EXCHANGED = 'already_exchanged'
|
||||||
REASONS = (
|
REASONS = (
|
||||||
(REASON_CANCELED, _('Order canceled')),
|
(REASON_CANCELED, _('Order canceled')),
|
||||||
(REASON_INVALID, _('Unknown ticket')),
|
(REASON_INVALID, _('Unknown ticket')),
|
||||||
@@ -366,6 +369,9 @@ class Checkin(models.Model):
|
|||||||
(REASON_UNAPPROVED, _('Order not approved')),
|
(REASON_UNAPPROVED, _('Order not approved')),
|
||||||
(REASON_INVALID_TIME, _('Ticket not valid at this time')),
|
(REASON_INVALID_TIME, _('Ticket not valid at this time')),
|
||||||
(REASON_ANNULLED, _('Check-in annulled')),
|
(REASON_ANNULLED, _('Check-in annulled')),
|
||||||
|
(REASON_ALREADY_EXCHANGED, _('Ticket already exchanged')),
|
||||||
|
(REASON_MEDIUM_INVALID, _('Reusable medium invalid')),
|
||||||
|
(REASON_MEDIUM_EXISTS, _('Reusable medium already exists')),
|
||||||
)
|
)
|
||||||
|
|
||||||
successful = models.BooleanField(
|
successful = models.BooleanField(
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ class Customer(LoggedModel):
|
|||||||
|
|
||||||
def send_security_notice(self, message, email=None):
|
def send_security_notice(self, message, email=None):
|
||||||
from pretix.base.services.mail import SendMailException, mail
|
from pretix.base.services.mail import SendMailException, mail
|
||||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
from pretix.multidomain.urlreverse import eventreverse_absolute
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with language(self.locale):
|
with language(self.locale):
|
||||||
@@ -178,7 +178,7 @@ class Customer(LoggedModel):
|
|||||||
{
|
{
|
||||||
**self.get_email_context(),
|
**self.get_email_context(),
|
||||||
'message': str(message),
|
'message': str(message),
|
||||||
'url': build_absolute_uri(self.organizer, 'presale:organizer.customer.index')
|
'url': eventreverse_absolute(self.organizer, 'presale:organizer.customer.index')
|
||||||
},
|
},
|
||||||
customer=self,
|
customer=self,
|
||||||
organizer=self.organizer,
|
organizer=self.organizer,
|
||||||
@@ -299,12 +299,12 @@ class Customer(LoggedModel):
|
|||||||
|
|
||||||
def send_activation_mail(self):
|
def send_activation_mail(self):
|
||||||
from pretix.base.services.mail import mail
|
from pretix.base.services.mail import mail
|
||||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
from pretix.multidomain.urlreverse import eventreverse_absolute
|
||||||
from pretix.presale.forms.customer import TokenGenerator
|
from pretix.presale.forms.customer import TokenGenerator
|
||||||
|
|
||||||
ctx = self.get_email_context()
|
ctx = self.get_email_context()
|
||||||
token = TokenGenerator().make_token(self)
|
token = TokenGenerator().make_token(self)
|
||||||
ctx['url'] = build_absolute_uri(
|
ctx['url'] = eventreverse_absolute(
|
||||||
self.organizer,
|
self.organizer,
|
||||||
'presale:organizer.customer.activate'
|
'presale:organizer.customer.activate'
|
||||||
) + '?id=' + self.identifier + '&token=' + token
|
) + '?id=' + self.identifier + '&token=' + token
|
||||||
@@ -395,7 +395,7 @@ class AttendeeProfile(models.Model):
|
|||||||
self.company,
|
self.company,
|
||||||
self.street,
|
self.street,
|
||||||
(self.zipcode or '') + ' ' + (self.city or '') + ' ' + (self.state_for_address or ''),
|
(self.zipcode or '') + ' ' + (self.city or '') + ' ' + (self.state_for_address or ''),
|
||||||
self.country.name,
|
self.country.name if self.country else None,
|
||||||
]
|
]
|
||||||
for a in self.answers:
|
for a in self.answers:
|
||||||
value = a.get('value')
|
value = a.get('value')
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import warnings
|
|||||||
from collections import Counter, OrderedDict, defaultdict
|
from collections import Counter, OrderedDict, defaultdict
|
||||||
from datetime import datetime, time, timedelta
|
from datetime import datetime, time, timedelta
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
from urllib.parse import urljoin
|
from urllib.parse import urljoin
|
||||||
from zoneinfo import ZoneInfo
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
@@ -79,10 +80,16 @@ from pretix.helpers.thumb import get_thumbnail
|
|||||||
from ..settings import settings_hierarkey
|
from ..settings import settings_hierarkey
|
||||||
from .organizer import Organizer, Team
|
from .organizer import Organizer, Team
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from hierarkey.proxy import HierarkeyProxy
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class EventMixin:
|
class EventMixin:
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
settings: HierarkeyProxy
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
if self.presale_start and self.presale_end and self.presale_start > self.presale_end:
|
if self.presale_start and self.presale_end and self.presale_start > self.presale_end:
|
||||||
raise ValidationError({'presale_end': _('The end of the presale period has to be later than its start.')})
|
raise ValidationError({'presale_end': _('The end of the presale period has to be later than its start.')})
|
||||||
@@ -724,7 +731,7 @@ class Event(EventMixin, LoggedModel):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def social_image(self):
|
def social_image(self):
|
||||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
from pretix.multidomain.urlreverse import eventreverse_absolute
|
||||||
|
|
||||||
img = None
|
img = None
|
||||||
logo_file = self.settings.get('logo_image', as_type=str, default='')[7:]
|
logo_file = self.settings.get('logo_image', as_type=str, default='')[7:]
|
||||||
@@ -742,7 +749,7 @@ class Event(EventMixin, LoggedModel):
|
|||||||
logger.exception(f'Failed to create thumbnail of {logo_file}')
|
logger.exception(f'Failed to create thumbnail of {logo_file}')
|
||||||
img = default_storage.url(logo_file)
|
img = default_storage.url(logo_file)
|
||||||
if img:
|
if img:
|
||||||
return urljoin(build_absolute_uri(self, 'presale:event.index'), img)
|
return urljoin(eventreverse_absolute(self, 'presale:event.index'), img)
|
||||||
|
|
||||||
def _seats(self, ignore_voucher=None):
|
def _seats(self, ignore_voucher=None):
|
||||||
from .seating import Seat
|
from .seating import Seat
|
||||||
@@ -883,6 +890,8 @@ class Event(EventMixin, LoggedModel):
|
|||||||
ItemProgramTime, ItemVariationMetaValue, Question, Quota,
|
ItemProgramTime, ItemVariationMetaValue, Question, Quota,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
is_cross_organizer = other.organizer_id != self.organizer_id
|
||||||
|
|
||||||
# Note: avoid self.set_active_plugins(), it causes trouble e.g. for the badges plugin.
|
# Note: avoid self.set_active_plugins(), it causes trouble e.g. for the badges plugin.
|
||||||
# Plugins can create data in installed() hook based on existing data of the event.
|
# Plugins can create data in installed() hook based on existing data of the event.
|
||||||
# Calling set_active_plugins() results in defaults being created while actually data
|
# Calling set_active_plugins() results in defaults being created while actually data
|
||||||
@@ -897,7 +906,7 @@ class Event(EventMixin, LoggedModel):
|
|||||||
self.save()
|
self.save()
|
||||||
self.log_action('pretix.object.cloned', data={'source': other.slug, 'source_id': other.pk})
|
self.log_action('pretix.object.cloned', data={'source': other.slug, 'source_id': other.pk})
|
||||||
|
|
||||||
if hasattr(other, 'alternative_domain_assignment'):
|
if hasattr(other, 'alternative_domain_assignment') and not is_cross_organizer:
|
||||||
other.alternative_domain_assignment.domain.event_assignments.create(event=self)
|
other.alternative_domain_assignment.domain.event_assignments.create(event=self)
|
||||||
|
|
||||||
if not self.all_sales_channels:
|
if not self.all_sales_channels:
|
||||||
@@ -911,6 +920,15 @@ class Event(EventMixin, LoggedModel):
|
|||||||
for emv in EventMetaValue.objects.filter(event=other):
|
for emv in EventMetaValue.objects.filter(event=other):
|
||||||
emv.pk = None
|
emv.pk = None
|
||||||
emv.event = self
|
emv.event = self
|
||||||
|
if is_cross_organizer:
|
||||||
|
try:
|
||||||
|
emv.property = self.organizer.meta_properties.get(name=emv.property.name)
|
||||||
|
except EventMetaProperty.DoesNotExist:
|
||||||
|
meta_prop = emv.property
|
||||||
|
meta_prop.pk = None
|
||||||
|
meta_prop.organizer = self.organizer
|
||||||
|
meta_prop.save(force_insert=True)
|
||||||
|
emv.property = meta_prop
|
||||||
emv.save(force_insert=True)
|
emv.save(force_insert=True)
|
||||||
|
|
||||||
for fl in EventFooterLink.objects.filter(event=other):
|
for fl in EventFooterLink.objects.filter(event=other):
|
||||||
@@ -964,13 +982,13 @@ class Event(EventMixin, LoggedModel):
|
|||||||
if i.tax_rule_id:
|
if i.tax_rule_id:
|
||||||
i.tax_rule = tax_map[i.tax_rule_id]
|
i.tax_rule = tax_map[i.tax_rule_id]
|
||||||
|
|
||||||
if i.grant_membership_type and other.organizer_id != self.organizer_id:
|
if i.grant_membership_type and is_cross_organizer:
|
||||||
i.grant_membership_type = None
|
i.grant_membership_type = None
|
||||||
|
|
||||||
i.save() # no force_insert since i.picture.save could have already inserted
|
i.save() # no force_insert since i.picture.save could have already inserted
|
||||||
i.log_action('pretix.object.cloned')
|
i.log_action('pretix.object.cloned')
|
||||||
|
|
||||||
if require_membership_types and other.organizer_id == self.organizer_id:
|
if require_membership_types and not is_cross_organizer:
|
||||||
i.require_membership_types.set(require_membership_types)
|
i.require_membership_types.set(require_membership_types)
|
||||||
|
|
||||||
if not i.all_sales_channels:
|
if not i.all_sales_channels:
|
||||||
@@ -985,7 +1003,7 @@ class Event(EventMixin, LoggedModel):
|
|||||||
v._prefetched_objects_cache = {}
|
v._prefetched_objects_cache = {}
|
||||||
v.save(force_insert=True)
|
v.save(force_insert=True)
|
||||||
|
|
||||||
if require_membership_types and other.organizer_id == self.organizer_id:
|
if require_membership_types and not is_cross_organizer:
|
||||||
v.require_membership_types.set(require_membership_types)
|
v.require_membership_types.set(require_membership_types)
|
||||||
if not v.all_sales_channels:
|
if not v.all_sales_channels:
|
||||||
v.limit_sales_channels.set(self.organizer.sales_channels.filter(identifier__in=[s.identifier for s in limit_sales_channels]))
|
v.limit_sales_channels.set(self.organizer.sales_channels.filter(identifier__in=[s.identifier for s in limit_sales_channels]))
|
||||||
@@ -1385,15 +1403,12 @@ class Event(EventMixin, LoggedModel):
|
|||||||
|
|
||||||
for mp in self.organizer.meta_properties.all():
|
for mp in self.organizer.meta_properties.all():
|
||||||
if mp.required and not self.meta_data.get(mp.name):
|
if mp.required and not self.meta_data.get(mp.name):
|
||||||
issues.append(
|
issues.append(format_html(
|
||||||
('<a {a_attr}>' + gettext('You need to fill the meta parameter "{property}".') + '</a>').format(
|
'<a href="{href}{href_hash}">{text}</a>',
|
||||||
property=mp.name,
|
text=gettext('You need to fill the meta parameter "{property}".').format(property=mp.name),
|
||||||
a_attr='href="%s#id_prop-%d-value"' % (
|
href=reverse('control:event.settings', kwargs={'organizer': self.organizer.slug, 'event': self.slug}),
|
||||||
reverse('control:event.settings', kwargs={'organizer': self.organizer.slug, 'event': self.slug}),
|
href_hash=f'#id_prop-{mp.pk}-value',
|
||||||
mp.pk
|
))
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
responses = event_live_issues.send(self)
|
responses = event_live_issues.send(self)
|
||||||
for receiver, response in sorted(responses, key=lambda r: str(r[0])):
|
for receiver, response in sorted(responses, key=lambda r: str(r[0])):
|
||||||
@@ -1869,6 +1884,8 @@ class EventMetaValue(LoggedModel):
|
|||||||
self.event.cache.clear()
|
self.event.cache.clear()
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
if self.event and self.event.organizer != self.property.organizer:
|
||||||
|
raise ValidationError(_("Property and event must belong to the same organizer."))
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
if self.event:
|
if self.event:
|
||||||
self.event.cache.clear()
|
self.event.cache.clear()
|
||||||
|
|||||||
@@ -452,11 +452,16 @@ class Item(LoggedModel):
|
|||||||
MEDIA_POLICY_REUSE = 'reuse'
|
MEDIA_POLICY_REUSE = 'reuse'
|
||||||
MEDIA_POLICY_NEW = 'new'
|
MEDIA_POLICY_NEW = 'new'
|
||||||
MEDIA_POLICY_REUSE_OR_NEW = 'reuse_or_new'
|
MEDIA_POLICY_REUSE_OR_NEW = 'reuse_or_new'
|
||||||
|
MEDIA_POLICY_APPEND = 'append'
|
||||||
|
MEDIA_POLICY_APPEND_OR_NEW = 'append_or_new'
|
||||||
MEDIA_POLICIES = (
|
MEDIA_POLICIES = (
|
||||||
(None, _("Don't use re-usable media, use regular one-off tickets")),
|
(None, _("Don't use reusable media, use regular one-off tickets")),
|
||||||
(MEDIA_POLICY_REUSE, _('Require an existing medium to be re-used')),
|
|
||||||
(MEDIA_POLICY_NEW, _('Require a previously unknown medium to be newly added')),
|
(MEDIA_POLICY_NEW, _('Require a previously unknown medium to be newly added')),
|
||||||
(MEDIA_POLICY_REUSE_OR_NEW, _('Require either an existing or a new medium to be used')),
|
(MEDIA_POLICY_REUSE, _('Require an existing medium to be reused, replacing any previous tickets')),
|
||||||
|
(MEDIA_POLICY_REUSE_OR_NEW, _('Require either an existing or a new medium to be used, replacing any previous tickets')),
|
||||||
|
(MEDIA_POLICY_APPEND, _('Require an existing medium to be reused, adding to any previous tickets')),
|
||||||
|
(MEDIA_POLICY_APPEND_OR_NEW,
|
||||||
|
_('Require either an existing or a new medium to be used, adding to any previous tickets')),
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = ItemQuerySetManager()
|
objects = ItemQuerySetManager()
|
||||||
@@ -769,7 +774,7 @@ class Item(LoggedModel):
|
|||||||
null=True, blank=True, max_length=16,
|
null=True, blank=True, max_length=16,
|
||||||
verbose_name=_('Reusable media policy'),
|
verbose_name=_('Reusable media policy'),
|
||||||
help_text=_(
|
help_text=_(
|
||||||
'If this product should be stored on a re-usable physical medium, you can attach a physical media policy. '
|
'If this product should be stored on a reusable physical medium, you can attach a physical media policy. '
|
||||||
'This is not required for regular tickets, which just use a one-time barcode, but only for products like '
|
'This is not required for regular tickets, which just use a one-time barcode, but only for products like '
|
||||||
'renewable season tickets or re-chargeable gift card wristbands. '
|
'renewable season tickets or re-chargeable gift card wristbands. '
|
||||||
'This is an advanced feature that also requires specific configuration of ticketing and printing settings.'
|
'This is an advanced feature that also requires specific configuration of ticketing and printing settings.'
|
||||||
@@ -778,7 +783,7 @@ class Item(LoggedModel):
|
|||||||
media_type = models.CharField(
|
media_type = models.CharField(
|
||||||
max_length=100,
|
max_length=100,
|
||||||
null=True, blank=True,
|
null=True, blank=True,
|
||||||
choices=[(None, _("Don't use re-usable media, use regular one-off tickets"))] + [(k, v) for k, v in MEDIA_TYPES.items()],
|
choices=[(None, _("Don't use reusable media, use regular one-off tickets"))] + [(k, v) for k, v in MEDIA_TYPES.items()],
|
||||||
verbose_name=_('Reusable media type'),
|
verbose_name=_('Reusable media type'),
|
||||||
help_text=_(
|
help_text=_(
|
||||||
'Select the type of physical medium that should be used for this product. Note that not all media types '
|
'Select the type of physical medium that should be used for this product. Note that not all media types '
|
||||||
@@ -995,6 +1000,11 @@ class Item(LoggedModel):
|
|||||||
raise ValidationError(_('The selected media type does not support usage for tickets currently.'))
|
raise ValidationError(_('The selected media type does not support usage for tickets currently.'))
|
||||||
if not mt.supports_giftcard and issue_giftcard:
|
if not mt.supports_giftcard and issue_giftcard:
|
||||||
raise ValidationError(_('The selected media type does not support usage for gift cards currently.'))
|
raise ValidationError(_('The selected media type does not support usage for gift cards currently.'))
|
||||||
|
if media_policy in (Item.MEDIA_POLICY_NEW, Item.MEDIA_POLICY_APPEND_OR_NEW, Item.MEDIA_POLICY_REUSE_OR_NEW):
|
||||||
|
if not mt.medium_created_by_server and not mt.medium_created_from_unknown_supported:
|
||||||
|
raise ValidationError(_('The selected media type requires all media to be registered in the system '
|
||||||
|
'prior to their usage. Therefore, the selected media policy does not make '
|
||||||
|
'sense for this media type.'))
|
||||||
if issue_giftcard:
|
if issue_giftcard:
|
||||||
raise ValidationError(_('You currently cannot create gift cards with a reusable media policy. Instead, '
|
raise ValidationError(_('You currently cannot create gift cards with a reusable media policy. Instead, '
|
||||||
'gift cards for some reusable media types can be created or re-charged directly '
|
'gift cards for some reusable media types can be created or re-charged directly '
|
||||||
@@ -2220,7 +2230,7 @@ class Quota(LoggedModel):
|
|||||||
class ItemMetaProperty(LoggedModel):
|
class ItemMetaProperty(LoggedModel):
|
||||||
"""
|
"""
|
||||||
An event can have ItemMetaProperty objects attached to define meta information fields
|
An event can have ItemMetaProperty objects attached to define meta information fields
|
||||||
for its items. This information can be re-used for example in ticket layouts.
|
for its items. This information can be reused for example in ticket layouts.
|
||||||
|
|
||||||
:param event: The event this property is defined for.
|
:param event: The event this property is defined for.
|
||||||
:type event: Event
|
:type event: Event
|
||||||
|
|||||||
@@ -129,7 +129,10 @@ class ReusableMedium(LoggedModel):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def is_expired(self):
|
def is_expired(self):
|
||||||
return self.expires and self.expires > now()
|
return self.expires and self.expires < now()
|
||||||
|
|
||||||
|
def touch(self):
|
||||||
|
self.save(update_fields=['updated'])
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = (("identifier", "type", "organizer"),)
|
unique_together = (("identifier", "type", "organizer"),)
|
||||||
|
|||||||
@@ -354,38 +354,60 @@ class Order(LockModel, LoggedModel):
|
|||||||
def _transaction_key_reset(self):
|
def _transaction_key_reset(self):
|
||||||
self.__initial_status_paid_or_pending = self.status in (Order.STATUS_PENDING, Order.STATUS_PAID) and not self.require_approval
|
self.__initial_status_paid_or_pending = self.status in (Order.STATUS_PENDING, Order.STATUS_PAID) and not self.require_approval
|
||||||
|
|
||||||
def gracefully_delete(self, user=None, auth=None):
|
@classmethod
|
||||||
from . import GiftCard, GiftCardTransaction, Membership, Voucher
|
def gracefully_delete_bulk(cls, event, orders, user=None, auth=None):
|
||||||
|
# Expects to be called in a transaction
|
||||||
if not self.testmode:
|
from . import (
|
||||||
raise TypeError("Only test mode orders can be deleted.")
|
GiftCard, GiftCardTransaction, LogEntry, Membership, Voucher,
|
||||||
self.log_action(
|
|
||||||
'pretix.event.order.deleted', user=user, auth=auth,
|
|
||||||
data={
|
|
||||||
'code': self.code,
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
order_gracefully_delete.send(self.event, order=self)
|
if not transaction.get_connection().in_atomic_block:
|
||||||
|
raise Exception('gracefully_delete_bulk should only be called in atomic transaction!')
|
||||||
|
|
||||||
if self.status != Order.STATUS_CANCELED:
|
logs_create = []
|
||||||
for position in self.positions.all():
|
for o in orders:
|
||||||
if position.voucher:
|
if not o.testmode:
|
||||||
Voucher.objects.filter(pk=position.voucher.pk).update(redeemed=Greatest(0, F('redeemed') - 1))
|
raise TypeError("Only test mode orders can be deleted.")
|
||||||
|
order_gracefully_delete.send(event, order=o)
|
||||||
|
logs_create.append(o.log_action(
|
||||||
|
'pretix.event.order.deleted', user=user, auth=auth,
|
||||||
|
data={
|
||||||
|
'code': o.code,
|
||||||
|
},
|
||||||
|
save=False,
|
||||||
|
))
|
||||||
|
LogEntry.bulk_create_and_postprocess(logs_create)
|
||||||
|
|
||||||
GiftCardTransaction.objects.filter(payment__in=self.payments.all()).update(payment=None)
|
voucher_ids = OrderPosition.objects.filter(
|
||||||
GiftCardTransaction.objects.filter(refund__in=self.refunds.all()).update(refund=None)
|
order__in=orders,
|
||||||
GiftCardTransaction.objects.filter(order=self).update(order=None)
|
voucher__isnull=False
|
||||||
GiftCard.objects.filter(issued_in__in=self.positions.all()).update(issued_in=None)
|
).exclude(order__status=Order.STATUS_CANCELED).values_list("voucher_id", flat=True)
|
||||||
Membership.objects.filter(granted_in__order=self, testmode=True).update(granted_in=None)
|
voucher_usages = Counter(voucher_ids)
|
||||||
OrderPosition.all.filter(order=self, addon_to__isnull=False).delete()
|
for v_id, usage_count in voucher_usages.items():
|
||||||
OrderPosition.all.filter(order=self).delete()
|
Voucher.objects.filter(pk=v_id).update(redeemed=Greatest(0, F('redeemed') - usage_count))
|
||||||
OrderFee.all.filter(order=self).delete()
|
|
||||||
Transaction.objects.filter(order=self).delete()
|
GiftCardTransaction.objects.filter(payment__order__in=orders).update(payment=None)
|
||||||
self.refunds.all().delete()
|
GiftCardTransaction.objects.filter(refund__order__in=orders).update(refund=None)
|
||||||
self.payments.all().delete()
|
GiftCardTransaction.objects.filter(order__in=orders).update(order=None)
|
||||||
self.event.cache.delete('complain_testmode_orders')
|
GiftCard.objects.filter(issued_in__order__in=orders).update(issued_in=None)
|
||||||
self.delete()
|
Membership.objects.filter(granted_in__order__in=orders, testmode=True).update(granted_in=None)
|
||||||
|
OrderPosition.all.filter(order__in=orders, addon_to__isnull=False).delete()
|
||||||
|
OrderPosition.all.filter(order__in=orders).delete()
|
||||||
|
OrderFee.all.filter(order__in=orders).delete()
|
||||||
|
Transaction.objects.filter(order__in=orders).delete()
|
||||||
|
OrderRefund.objects.filter(order__in=orders).delete()
|
||||||
|
OrderPayment.objects.filter(order__in=orders).delete()
|
||||||
|
if isinstance(orders, models.QuerySet):
|
||||||
|
orders.delete()
|
||||||
|
else:
|
||||||
|
Order.objects.filter(pk__in=[o.pk for o in orders]).delete()
|
||||||
|
event.cache.delete('complain_testmode_orders')
|
||||||
|
|
||||||
|
def gracefully_delete(self, user=None, auth=None):
|
||||||
|
if not self.testmode:
|
||||||
|
raise TypeError("Only test mode orders can be deleted.")
|
||||||
|
|
||||||
|
Order.gracefully_delete_bulk(self.event, Order.objects.filter(pk=self.pk), user, auth)
|
||||||
|
|
||||||
def email_confirm_secret(self):
|
def email_confirm_secret(self):
|
||||||
return self.tagged_secret("email_confirm", 9)
|
return self.tagged_secret("email_confirm", 9)
|
||||||
@@ -1675,7 +1697,7 @@ class AbstractPosition(RoundingCorrectionMixin, models.Model):
|
|||||||
self.company,
|
self.company,
|
||||||
self.street,
|
self.street,
|
||||||
(self.zipcode or '') + ' ' + (self.city or '') + ' ' + (self.state_for_address or ''),
|
(self.zipcode or '') + ' ' + (self.city or '') + ' ' + (self.state_for_address or ''),
|
||||||
self.country.name
|
self.country.name if self.country else ''
|
||||||
]
|
]
|
||||||
lines = [r.strip() for r in lines if r]
|
lines = [r.strip() for r in lines if r]
|
||||||
return '\n'.join(lines).strip()
|
return '\n'.join(lines).strip()
|
||||||
@@ -3416,7 +3438,7 @@ class InvoiceAddress(models.Model):
|
|||||||
self.name,
|
self.name,
|
||||||
self.street,
|
self.street,
|
||||||
(self.zipcode or '') + ' ' + (self.city or '') + ' ' + (self.state_for_address or ''),
|
(self.zipcode or '') + ' ' + (self.city or '') + ' ' + (self.state_for_address or ''),
|
||||||
self.country.name,
|
self.country.name if self.country else '',
|
||||||
self.vat_id,
|
self.vat_id,
|
||||||
self.custom_field,
|
self.custom_field,
|
||||||
self.internal_reference,
|
self.internal_reference,
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import operator
|
|||||||
import string
|
import string
|
||||||
from datetime import date, datetime, time
|
from datetime import date, datetime, time
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import pytz_deprecation_shim
|
import pytz_deprecation_shim
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@@ -61,6 +62,9 @@ from ...helpers.permission_migration import (
|
|||||||
from ..settings import settings_hierarkey
|
from ..settings import settings_hierarkey
|
||||||
from .auth import User
|
from .auth import User
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from hierarkey.proxy import HierarkeyProxy
|
||||||
|
|
||||||
|
|
||||||
@settings_hierarkey.add(cache_namespace='organizer')
|
@settings_hierarkey.add(cache_namespace='organizer')
|
||||||
class Organizer(LoggedModel):
|
class Organizer(LoggedModel):
|
||||||
@@ -78,6 +82,9 @@ class Organizer(LoggedModel):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
settings_namespace = 'organizer'
|
settings_namespace = 'organizer'
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
settings: HierarkeyProxy
|
||||||
|
|
||||||
name = models.CharField(max_length=200,
|
name = models.CharField(max_length=200,
|
||||||
verbose_name=_("Name"))
|
verbose_name=_("Name"))
|
||||||
slug = models.CharField(
|
slug = models.CharField(
|
||||||
|
|||||||
@@ -118,7 +118,10 @@ class SeatingPlan(LoggedModel):
|
|||||||
for zi, z in enumerate(self.layout_data['zones']):
|
for zi, z in enumerate(self.layout_data['zones']):
|
||||||
zpos = (z['position']['x'], z['position']['y'])
|
zpos = (z['position']['x'], z['position']['y'])
|
||||||
for ri, r in enumerate(z['rows']):
|
for ri, r in enumerate(z['rows']):
|
||||||
rpos = (zpos[0] + r['position']['x'], zpos[1] + r['position']['y'])
|
rpos = (
|
||||||
|
zpos[0] + r.get('position', {}).get('x', 0),
|
||||||
|
zpos[1] + r.get('position', {}).get('y', 0),
|
||||||
|
)
|
||||||
row_label = None
|
row_label = None
|
||||||
if r.get('row_label'):
|
if r.get('row_label'):
|
||||||
row_label = r['row_label'].replace("%s", r.get('row_number', str(ri)))
|
row_label = r['row_label'].replace("%s", r.get('row_number', str(ri)))
|
||||||
@@ -147,8 +150,8 @@ class SeatingPlan(LoggedModel):
|
|||||||
zone=z['name'],
|
zone=z['name'],
|
||||||
category=s['category'],
|
category=s['category'],
|
||||||
sorting_rank=rank,
|
sorting_rank=rank,
|
||||||
x=rpos[0] + s['position']['x'],
|
x=rpos[0] + s.get('position', {}).get('x', 0),
|
||||||
y=rpos[1] + s['position']['y'],
|
y=rpos[1] + s.get('position', {}).get('y', 0),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ from django.utils.translation import gettext_lazy as _, pgettext_lazy
|
|||||||
from pretix.base.models import Event, LogEntry
|
from pretix.base.models import Event, LogEntry
|
||||||
from pretix.base.signals import register_notification_types
|
from pretix.base.signals import register_notification_types
|
||||||
from pretix.base.templatetags.money import money_filter
|
from pretix.base.templatetags.money import money_filter
|
||||||
from pretix.helpers.urls import build_absolute_uri
|
from pretix.helpers.urls import mainreverse_absolute
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
_ALL_TYPES = None
|
_ALL_TYPES = None
|
||||||
@@ -170,7 +170,7 @@ class ParametrizedOrderNotificationType(NotificationType):
|
|||||||
def build_notification(self, logentry: LogEntry):
|
def build_notification(self, logentry: LogEntry):
|
||||||
order = logentry.content_object
|
order = logentry.content_object
|
||||||
|
|
||||||
order_url = build_absolute_uri(
|
order_url = mainreverse_absolute(
|
||||||
'control:event.order',
|
'control:event.order',
|
||||||
kwargs={
|
kwargs={
|
||||||
'organizer': logentry.event.organizer.slug,
|
'organizer': logentry.event.organizer.slug,
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ from pretix.helpers import OF_SELF
|
|||||||
from pretix.helpers.countries import CachedCountries
|
from pretix.helpers.countries import CachedCountries
|
||||||
from pretix.helpers.format import format_map
|
from pretix.helpers.format import format_map
|
||||||
from pretix.helpers.money import DecimalTextInput
|
from pretix.helpers.money import DecimalTextInput
|
||||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
from pretix.multidomain.urlreverse import eventreverse_absolute
|
||||||
from pretix.presale.views import get_cart
|
from pretix.presale.views import get_cart
|
||||||
from pretix.presale.views.cart import cart_session, get_or_create_cart_id
|
from pretix.presale.views.cart import cart_session, get_or_create_cart_id
|
||||||
|
|
||||||
@@ -379,7 +379,7 @@ class BasePaymentProvider:
|
|||||||
|
|
||||||
if not self.settings.get('_hidden_seed'):
|
if not self.settings.get('_hidden_seed'):
|
||||||
self.settings.set('_hidden_seed', get_random_string(64))
|
self.settings.set('_hidden_seed', get_random_string(64))
|
||||||
hidden_url = build_absolute_uri(self.event, 'presale:event.payment.unlock', kwargs={
|
hidden_url = eventreverse_absolute(self.event, 'presale:event.payment.unlock', kwargs={
|
||||||
'hash': hashlib.sha256((self.settings._hidden_seed + self.event.slug).encode()).hexdigest(),
|
'hash': hashlib.sha256((self.settings._hidden_seed + self.event.slug).encode()).hexdigest(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -372,6 +372,11 @@ DEFAULT_VARIABLES = OrderedDict((
|
|||||||
"editor_sample": _("Atlantis"),
|
"editor_sample": _("Atlantis"),
|
||||||
"evaluate": lambda op, order, ev: str(getattr(order.invoice_address.country, 'name', '')) if getattr(order, 'invoice_address', None) else ''
|
"evaluate": lambda op, order, ev: str(getattr(order.invoice_address.country, 'name', '')) if getattr(order, 'invoice_address', None) else ''
|
||||||
}),
|
}),
|
||||||
|
("invoice_custom_field", {
|
||||||
|
"label": _("Invoice custom recipient field"),
|
||||||
|
"editor_sample": _("Custom recipient field"),
|
||||||
|
"evaluate": lambda op, order, ev: order.invoice_address.custom_field if getattr(order, 'invoice_address', None) else ''
|
||||||
|
}),
|
||||||
("addons", {
|
("addons", {
|
||||||
"label": _("List of Add-Ons"),
|
"label": _("List of Add-Ons"),
|
||||||
"editor_sample": _("Add-on 1\n2x Add-on 2"),
|
"editor_sample": _("Add-on 1\n2x Add-on 2"),
|
||||||
@@ -1062,7 +1067,7 @@ class Renderer:
|
|||||||
except:
|
except:
|
||||||
logger.exception('Reshaping/Bidi fixes failed on string {}'.format(repr(text)))
|
logger.exception('Reshaping/Bidi fixes failed on string {}'.format(repr(text)))
|
||||||
|
|
||||||
p = Paragraph(text, style=style)
|
p = Paragraph(text, style=style) # not using AutoEscapeParagraph is safe as we escape above
|
||||||
return p, ad, lineheight
|
return p, ad, lineheight
|
||||||
|
|
||||||
def _draw_textcontainer(self, canvas: Canvas, op: OrderPosition, order: Order, o: dict):
|
def _draw_textcontainer(self, canvas: Canvas, op: OrderPosition, order: Order, o: dict):
|
||||||
|
|||||||
@@ -287,11 +287,11 @@ def _check_position_constraints(
|
|||||||
raise CartPositionError(error_messages['unavailable'])
|
raise CartPositionError(error_messages['unavailable'])
|
||||||
|
|
||||||
# Invalid media policy for online sale
|
# Invalid media policy for online sale
|
||||||
if item.media_policy in (Item.MEDIA_POLICY_NEW, Item.MEDIA_POLICY_REUSE_OR_NEW):
|
if item.media_policy in (Item.MEDIA_POLICY_NEW, Item.MEDIA_POLICY_REUSE_OR_NEW, Item.MEDIA_POLICY_APPEND_OR_NEW, Item.MEDIA_POLICY_REUSE_OR_NEW):
|
||||||
mt = MEDIA_TYPES[item.media_type]
|
mt = MEDIA_TYPES[item.media_type]
|
||||||
if not mt.medium_created_by_server:
|
if not mt.medium_created_by_server:
|
||||||
raise CartPositionError(error_messages['media_usage_not_implemented'])
|
raise CartPositionError(error_messages['media_usage_not_implemented'])
|
||||||
elif item.media_policy == Item.MEDIA_POLICY_REUSE:
|
elif item.media_policy in (Item.MEDIA_POLICY_REUSE, Item.MEDIA_POLICY_APPEND):
|
||||||
raise CartPositionError(error_messages['media_usage_not_implemented'])
|
raise CartPositionError(error_messages['media_usage_not_implemented'])
|
||||||
|
|
||||||
# Item removed from sales channel
|
# Item removed from sales channel
|
||||||
|
|||||||
@@ -867,6 +867,15 @@ class RequiredQuestionsError(Exception):
|
|||||||
super().__init__(msg)
|
super().__init__(msg)
|
||||||
|
|
||||||
|
|
||||||
|
class RequiredMediaExchangeError(Exception):
|
||||||
|
def __init__(self, msg, code, media_policy, media_type):
|
||||||
|
self.msg = msg
|
||||||
|
self.code = code
|
||||||
|
self.media_policy = media_policy
|
||||||
|
self.media_type = media_type
|
||||||
|
super().__init__(msg)
|
||||||
|
|
||||||
|
|
||||||
def _save_answers(op, answers, given_answers):
|
def _save_answers(op, answers, given_answers):
|
||||||
def _create_answer(question, answer):
|
def _create_answer(question, answer):
|
||||||
try:
|
try:
|
||||||
@@ -939,7 +948,7 @@ def perform_checkin(op: OrderPosition, clist: CheckinList, given_answers: dict,
|
|||||||
ignore_unpaid=False, nonce=None, datetime=None, questions_supported=True,
|
ignore_unpaid=False, nonce=None, datetime=None, questions_supported=True,
|
||||||
user=None, auth=None, canceled_supported=False, type=Checkin.TYPE_ENTRY,
|
user=None, auth=None, canceled_supported=False, type=Checkin.TYPE_ENTRY,
|
||||||
raw_barcode=None, raw_source_type=None, from_revoked_secret=False, simulate=False,
|
raw_barcode=None, raw_source_type=None, from_revoked_secret=False, simulate=False,
|
||||||
gate=None):
|
gate=None, reusable_medium=None):
|
||||||
"""
|
"""
|
||||||
Create a checkin for this particular order position and check-in list. Fails with CheckInError if the check in is
|
Create a checkin for this particular order position and check-in list. Fails with CheckInError if the check in is
|
||||||
not valid at this time.
|
not valid at this time.
|
||||||
@@ -955,6 +964,7 @@ def perform_checkin(op: OrderPosition, clist: CheckinList, given_answers: dict,
|
|||||||
:param datetime: The datetime of the checkin, defaults to now.
|
:param datetime: The datetime of the checkin, defaults to now.
|
||||||
:param simulate: If true, the check-in is not saved.
|
:param simulate: If true, the check-in is not saved.
|
||||||
:param gate: The gate the check-in was performed at.
|
:param gate: The gate the check-in was performed at.
|
||||||
|
:param reusable_medium: The medium that is available for an exchange
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# !!!!!!!!!
|
# !!!!!!!!!
|
||||||
@@ -1035,7 +1045,7 @@ def perform_checkin(op: OrderPosition, clist: CheckinList, given_answers: dict,
|
|||||||
|
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
# Lock order positions, if it is an entry. We don't need it for exits, as a race condition wouldn't be problematic
|
# Lock order positions, if it is an entry. We don't need it for exits, as a race condition wouldn't be problematic
|
||||||
opqs = OrderPosition.all
|
opqs = OrderPosition.all.select_related("order", "item")
|
||||||
if type != Checkin.TYPE_EXIT:
|
if type != Checkin.TYPE_EXIT:
|
||||||
opqs = opqs.select_for_update(of=OF_SELF)
|
opqs = opqs.select_for_update(of=OF_SELF)
|
||||||
op = opqs.get(pk=op.pk)
|
op = opqs.get(pk=op.pk)
|
||||||
@@ -1101,6 +1111,24 @@ def perform_checkin(op: OrderPosition, clist: CheckinList, given_answers: dict,
|
|||||||
require_answers
|
require_answers
|
||||||
)
|
)
|
||||||
|
|
||||||
|
required_media_policy = op.item.media_policy
|
||||||
|
required_media_type = op.item.media_type
|
||||||
|
require_a_medium = required_media_policy and required_media_type
|
||||||
|
linked_media = op.linked_media
|
||||||
|
if require_a_medium and not reusable_medium and not force:
|
||||||
|
if not linked_media.exists():
|
||||||
|
raise RequiredMediaExchangeError(
|
||||||
|
_('Ticket needs to be exchanged to a suitable medium.'),
|
||||||
|
'exchange',
|
||||||
|
required_media_policy,
|
||||||
|
required_media_type
|
||||||
|
)
|
||||||
|
elif op.organizer.settings.reusable_media_usage_enforced:
|
||||||
|
raise CheckInError(
|
||||||
|
_('This ticket has already been exchanged for a reusable medium that now needs to be used instead.'),
|
||||||
|
'already_exchanged',
|
||||||
|
)
|
||||||
|
|
||||||
device = None
|
device = None
|
||||||
if isinstance(auth, Device):
|
if isinstance(auth, Device):
|
||||||
device = auth
|
device = auth
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ from pretix.base.signals import (
|
|||||||
)
|
)
|
||||||
from pretix.celery_app import app
|
from pretix.celery_app import app
|
||||||
from pretix.helpers import OF_SELF, repeatable_reads_transaction
|
from pretix.helpers import OF_SELF, repeatable_reads_transaction
|
||||||
from pretix.helpers.urls import build_absolute_uri
|
from pretix.helpers.urls import mainreverse_absolute
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -455,7 +455,7 @@ def scheduled_organizer_export(self, organizer: Organizer, schedule: int) -> Non
|
|||||||
schedule,
|
schedule,
|
||||||
organizer,
|
organizer,
|
||||||
exporter,
|
exporter,
|
||||||
build_absolute_uri(
|
mainreverse_absolute(
|
||||||
'control:organizer.export',
|
'control:organizer.export',
|
||||||
kwargs={
|
kwargs={
|
||||||
'organizer': organizer.slug,
|
'organizer': organizer.slug,
|
||||||
@@ -481,7 +481,7 @@ def scheduled_event_export(self, event: Event, schedule: int) -> None:
|
|||||||
schedule,
|
schedule,
|
||||||
event,
|
event,
|
||||||
exporter,
|
exporter,
|
||||||
build_absolute_uri(
|
mainreverse_absolute(
|
||||||
'control:event.orders.export',
|
'control:event.orders.export',
|
||||||
kwargs={
|
kwargs={
|
||||||
'event': event.slug,
|
'event': event.slug,
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ from pretix.helpers.format import (
|
|||||||
FormattedString, PlainHtmlAlternativeString, SafeFormatter, format_map,
|
FormattedString, PlainHtmlAlternativeString, SafeFormatter, format_map,
|
||||||
)
|
)
|
||||||
from pretix.helpers.hierarkey import clean_filename
|
from pretix.helpers.hierarkey import clean_filename
|
||||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
from pretix.multidomain.urlreverse import eventreverse_absolute
|
||||||
from pretix.presale.ical import get_private_icals
|
from pretix.presale.ical import get_private_icals
|
||||||
|
|
||||||
logger = logging.getLogger('pretix.base.mail')
|
logger = logging.getLogger('pretix.base.mail')
|
||||||
@@ -997,7 +997,7 @@ def _wrap_plain_body(content_plain, signature, event, order, position, no_order_
|
|||||||
body_plain += _(
|
body_plain += _(
|
||||||
"You can view your order details at the following URL:\n{orderurl}."
|
"You can view your order details at the following URL:\n{orderurl}."
|
||||||
).replace("\n", "\r\n").format(
|
).replace("\n", "\r\n").format(
|
||||||
orderurl=build_absolute_uri(
|
orderurl=eventreverse_absolute(
|
||||||
order.event, 'presale:event.order.position', kwargs={
|
order.event, 'presale:event.order.position', kwargs={
|
||||||
'order': order.code,
|
'order': order.code,
|
||||||
'secret': position.web_secret,
|
'secret': position.web_secret,
|
||||||
@@ -1013,7 +1013,7 @@ def _wrap_plain_body(content_plain, signature, event, order, position, no_order_
|
|||||||
body_plain += _(
|
body_plain += _(
|
||||||
"You can view your order details at the following URL:\n{orderurl}."
|
"You can view your order details at the following URL:\n{orderurl}."
|
||||||
).replace("\n", "\r\n").format(
|
).replace("\n", "\r\n").format(
|
||||||
event=event.name, orderurl=build_absolute_uri(
|
event=event.name, orderurl=eventreverse_absolute(
|
||||||
order.event, 'presale:event.order.open', kwargs={
|
order.event, 'presale:event.order.open', kwargs={
|
||||||
'order': order.code,
|
'order': order.code,
|
||||||
'secret': order.secret,
|
'secret': order.secret,
|
||||||
|
|||||||
@@ -23,10 +23,13 @@ import secrets
|
|||||||
|
|
||||||
from django.db import IntegrityError
|
from django.db import IntegrityError
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
from django_scopes import scopes_disabled
|
from django_scopes import scopes_disabled
|
||||||
|
|
||||||
from pretix.base.models import GiftCardAcceptance
|
from pretix.base.media import MEDIA_TYPES
|
||||||
from pretix.base.models.media import MediumKeySet
|
from pretix.base.models import Checkin, GiftCardAcceptance, Item
|
||||||
|
from pretix.base.models.media import MediumKeySet, ReusableMedium
|
||||||
|
from pretix.base.services.checkin import CheckInError
|
||||||
|
|
||||||
|
|
||||||
def create_nfc_mf0aes_keyset(organizer):
|
def create_nfc_mf0aes_keyset(organizer):
|
||||||
@@ -70,3 +73,174 @@ def get_keysets_for_organizer(organizer):
|
|||||||
if new_set:
|
if new_set:
|
||||||
sets.append(new_set)
|
sets.append(new_set)
|
||||||
return sets
|
return sets
|
||||||
|
|
||||||
|
|
||||||
|
def perform_media_exchange(organizer, media_type, identifier, link_orderposition, user, auth):
|
||||||
|
"""
|
||||||
|
Create or retrieve a medium, then link the order position to it. Expected to be called in a transaction.
|
||||||
|
|
||||||
|
:param organizer: Organizer to operate in
|
||||||
|
:param media_type: Type of medium to operate with
|
||||||
|
:param identifier: Identifier of the medium
|
||||||
|
:param link_orderposition: Position to link to the medium
|
||||||
|
:return: ReusableMedium
|
||||||
|
"""
|
||||||
|
medium = None
|
||||||
|
media_policy = link_orderposition.item.media_policy
|
||||||
|
|
||||||
|
if media_type not in MEDIA_TYPES: # should be caught by serializer already
|
||||||
|
raise CheckInError(
|
||||||
|
_('Invalid medium type.'),
|
||||||
|
Checkin.REASON_ERROR,
|
||||||
|
reason=_('Invalid medium type.'),
|
||||||
|
)
|
||||||
|
|
||||||
|
if not MEDIA_TYPES[media_type].is_active(organizer):
|
||||||
|
raise CheckInError(
|
||||||
|
_('Medium type is not enabled for organizer.'),
|
||||||
|
Checkin.REASON_ERROR,
|
||||||
|
reason=_('Medium type is not enabled for organizer.'),
|
||||||
|
)
|
||||||
|
|
||||||
|
if link_orderposition.item.media_type != media_type:
|
||||||
|
raise CheckInError(
|
||||||
|
_('Incorrect medium type for product.'),
|
||||||
|
Checkin.REASON_PRODUCT,
|
||||||
|
reason=_('Incorrect medium type for product.'),
|
||||||
|
)
|
||||||
|
|
||||||
|
if link_orderposition.linked_media.exists():
|
||||||
|
raise CheckInError(
|
||||||
|
_('Ticket is already exchanged for reusable medium.'),
|
||||||
|
Checkin.REASON_ALREADY_EXCHANGED,
|
||||||
|
reason=_('Ticket is already exchanged for reusable medium.'),
|
||||||
|
)
|
||||||
|
|
||||||
|
if media_policy in (Item.MEDIA_POLICY_APPEND, Item.MEDIA_POLICY_APPEND_OR_NEW, Item.MEDIA_POLICY_NEW):
|
||||||
|
link_action = "append"
|
||||||
|
else:
|
||||||
|
link_action = "replace"
|
||||||
|
|
||||||
|
if media_policy in (Item.MEDIA_POLICY_REUSE, Item.MEDIA_POLICY_APPEND):
|
||||||
|
try:
|
||||||
|
medium = ReusableMedium.objects.get(
|
||||||
|
type=media_type,
|
||||||
|
identifier=identifier,
|
||||||
|
organizer=organizer,
|
||||||
|
)
|
||||||
|
except ReusableMedium.DoesNotExist:
|
||||||
|
raise CheckInError(
|
||||||
|
_('Reusable medium not found.'),
|
||||||
|
Checkin.REASON_MEDIUM_INVALID,
|
||||||
|
reason=_('Reusable medium not found.'),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if medium.is_expired or not medium.active:
|
||||||
|
raise CheckInError(
|
||||||
|
_('Reusable medium is inactive or expired.'),
|
||||||
|
Checkin.REASON_MEDIUM_INVALID,
|
||||||
|
reason=_('Reusable medium is inactive or expired.'),
|
||||||
|
)
|
||||||
|
|
||||||
|
elif media_policy in (Item.MEDIA_POLICY_REUSE_OR_NEW, Item.MEDIA_POLICY_APPEND_OR_NEW):
|
||||||
|
try:
|
||||||
|
medium = ReusableMedium.objects.get(
|
||||||
|
type=media_type,
|
||||||
|
identifier=identifier,
|
||||||
|
organizer=organizer,
|
||||||
|
)
|
||||||
|
except ReusableMedium.DoesNotExist:
|
||||||
|
if not MEDIA_TYPES[media_type].medium_created_from_unknown_supported:
|
||||||
|
raise CheckInError(
|
||||||
|
_('Reusable medium not found and could not be created.'),
|
||||||
|
Checkin.REASON_MEDIUM_INVALID,
|
||||||
|
)
|
||||||
|
|
||||||
|
medium = MEDIA_TYPES[media_type].handle_unknown(organizer, identifier, user, auth, force_create=True)
|
||||||
|
if not medium:
|
||||||
|
raise CheckInError(
|
||||||
|
_('Reusable medium not found and could not be created.'),
|
||||||
|
Checkin.REASON_MEDIUM_INVALID,
|
||||||
|
)
|
||||||
|
|
||||||
|
if medium.is_expired or not medium.active:
|
||||||
|
raise CheckInError(
|
||||||
|
_('Reusable medium is inactive or expired.'),
|
||||||
|
Checkin.REASON_MEDIUM_INVALID,
|
||||||
|
reason=_('Reusable medium is inactive or expired.'),
|
||||||
|
)
|
||||||
|
|
||||||
|
elif media_policy == Item.MEDIA_POLICY_NEW:
|
||||||
|
if not MEDIA_TYPES[media_type].medium_created_from_unknown_supported:
|
||||||
|
raise CheckInError(
|
||||||
|
_('Reusable medium not found and could not be created.'),
|
||||||
|
Checkin.REASON_MEDIUM_INVALID,
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
medium = MEDIA_TYPES[media_type].handle_unknown(organizer, identifier, user, auth, force_create=True)
|
||||||
|
except IntegrityError:
|
||||||
|
raise CheckInError(
|
||||||
|
_('Reusable medium already exists.'),
|
||||||
|
Checkin.REASON_MEDIUM_EXISTS,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if not medium:
|
||||||
|
raise CheckInError(
|
||||||
|
_('Reusable medium could not be created.'),
|
||||||
|
Checkin.REASON_MEDIUM_INVALID,
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise CheckInError(
|
||||||
|
_('Product does not support medium exchange.'),
|
||||||
|
Checkin.REASON_PRODUCT,
|
||||||
|
reason=_('Product does not support medium exchange.'),
|
||||||
|
)
|
||||||
|
|
||||||
|
if link_action == 'append':
|
||||||
|
medium.linked_orderpositions.add(link_orderposition)
|
||||||
|
medium.log_action(
|
||||||
|
'pretix.reusable_medium.linked_orderposition.added',
|
||||||
|
user=user,
|
||||||
|
auth=auth,
|
||||||
|
data={
|
||||||
|
'linked_orderposition': link_orderposition,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
elif link_action == 'replace':
|
||||||
|
already_found = False
|
||||||
|
for op_pk in medium.linked_orderpositions.values_list('pk', flat=True):
|
||||||
|
if op_pk == link_orderposition.pk:
|
||||||
|
already_found = True
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
medium.log_action(
|
||||||
|
'pretix.reusable_medium.linked_orderposition.removed',
|
||||||
|
data={
|
||||||
|
'linked_orderposition': op_pk,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if not already_found:
|
||||||
|
medium.linked_orderpositions.set([link_orderposition])
|
||||||
|
medium.log_action(
|
||||||
|
'pretix.reusable_medium.linked_orderposition.added',
|
||||||
|
user=user,
|
||||||
|
auth=auth,
|
||||||
|
data={
|
||||||
|
'linked_orderposition': link_orderposition,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
link_orderposition.order.log_action(
|
||||||
|
'pretix.reusable_medium.exchanged',
|
||||||
|
data={
|
||||||
|
'position': link_orderposition.pk,
|
||||||
|
'positionid': link_orderposition.positionid,
|
||||||
|
'medium': medium.pk,
|
||||||
|
'medium_identifier': medium.identifier,
|
||||||
|
'medium_type': medium.media_type.identifier,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
medium.touch()
|
||||||
|
|
||||||
|
return medium
|
||||||
|
|||||||
@@ -26,8 +26,9 @@ from typing import List
|
|||||||
from django.conf import settings as django_settings
|
from django.conf import settings as django_settings
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
from django.db.utils import IntegrityError
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _, ngettext
|
||||||
|
|
||||||
from pretix.base.i18n import language
|
from pretix.base.i18n import language
|
||||||
from pretix.base.modelimport import DataImportError, ImportColumn, parse_csv
|
from pretix.base.modelimport import DataImportError, ImportColumn, parse_csv
|
||||||
@@ -260,6 +261,7 @@ def import_vouchers(event: Event, fileid: str, settings: dict, locale: str, user
|
|||||||
# Prepare model objects. Yes, this might consume lots of RAM, but allows us to make the actual SQL transaction
|
# Prepare model objects. Yes, this might consume lots of RAM, but allows us to make the actual SQL transaction
|
||||||
# shorter. We'll see what works better in reality…
|
# shorter. We'll see what works better in reality…
|
||||||
vouchers = []
|
vouchers = []
|
||||||
|
codes = set()
|
||||||
lock_seats = []
|
lock_seats = []
|
||||||
for i, record in enumerate(data):
|
for i, record in enumerate(data):
|
||||||
try:
|
try:
|
||||||
@@ -268,6 +270,14 @@ def import_vouchers(event: Event, fileid: str, settings: dict, locale: str, user
|
|||||||
|
|
||||||
if not record.get("code"):
|
if not record.get("code"):
|
||||||
raise ValidationError(_('A voucher cannot be created without a code.'))
|
raise ValidationError(_('A voucher cannot be created without a code.'))
|
||||||
|
code = record.get("code")
|
||||||
|
if code.upper() in codes:
|
||||||
|
raise ValidationError(
|
||||||
|
_('Voucher codes must be unique. Code "{code}" already exists in this import.').format(
|
||||||
|
code=code,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
codes.add(code.upper())
|
||||||
Voucher.clean_item_properties(
|
Voucher.clean_item_properties(
|
||||||
record,
|
record,
|
||||||
event,
|
event,
|
||||||
@@ -286,8 +296,22 @@ def import_vouchers(event: Event, fileid: str, settings: dict, locale: str, user
|
|||||||
lock_seats.append(voucher.seat)
|
lock_seats.append(voucher.seat)
|
||||||
except (ValidationError, ImportError) as e:
|
except (ValidationError, ImportError) as e:
|
||||||
raise DataImportError(
|
raise DataImportError(
|
||||||
_('Invalid data in row {row}: {message}').format(row=i, message=str(e))
|
_('Invalid data in row {row}: {message}').format(row=i + 1, message=str(e))
|
||||||
)
|
)
|
||||||
|
existing_codes = Voucher.objects.filter(
|
||||||
|
event=event,
|
||||||
|
code__in=codes,
|
||||||
|
).values_list("code", flat=True)
|
||||||
|
if len(existing_codes):
|
||||||
|
raise DataImportError(
|
||||||
|
ngettext(
|
||||||
|
'Voucher codes must be unique. Import contains existing voucher code {code}.',
|
||||||
|
'Voucher codes must be unique. Import contains existing voucher codes {code}.',
|
||||||
|
len(existing_codes)
|
||||||
|
).format(
|
||||||
|
code=", ".join(existing_codes)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
# We don't support quotas here, so we only need to lock if seats are in use
|
# We don't support quotas here, so we only need to lock if seats are in use
|
||||||
@@ -300,7 +324,13 @@ def import_vouchers(event: Event, fileid: str, settings: dict, locale: str, user
|
|||||||
|
|
||||||
save_logentries = []
|
save_logentries = []
|
||||||
for v in vouchers:
|
for v in vouchers:
|
||||||
v.save()
|
try:
|
||||||
|
v.save()
|
||||||
|
except IntegrityError:
|
||||||
|
# should not happen as we check existing codes before, but we did not lock so we might have a race-condition
|
||||||
|
raise DataImportError(
|
||||||
|
_('Vouchers could not be imported, probably due to a voucher code already being in use.')
|
||||||
|
)
|
||||||
save_logentries.append(v.log_action(
|
save_logentries.append(v.log_action(
|
||||||
'pretix.voucher.added',
|
'pretix.voucher.added',
|
||||||
user=user,
|
user=user,
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ from pretix.base.services.tasks import ProfiledTask, TransactionAwareTask
|
|||||||
from pretix.base.signals import notification
|
from pretix.base.signals import notification
|
||||||
from pretix.celery_app import app
|
from pretix.celery_app import app
|
||||||
from pretix.helpers.celery import get_task_priority
|
from pretix.helpers.celery import get_task_priority
|
||||||
from pretix.helpers.urls import build_absolute_uri
|
from pretix.helpers.urls import mainreverse_absolute
|
||||||
|
|
||||||
|
|
||||||
@app.task(base=TransactionAwareTask, acks_late=True, max_retries=9, default_retry_delay=900)
|
@app.task(base=TransactionAwareTask, acks_late=True, max_retries=9, default_retry_delay=900)
|
||||||
@@ -136,10 +136,10 @@ def send_notification_mail(notification: Notification, user: User):
|
|||||||
'site_url': settings.SITE_URL,
|
'site_url': settings.SITE_URL,
|
||||||
'color': settings.PRETIX_PRIMARY_COLOR,
|
'color': settings.PRETIX_PRIMARY_COLOR,
|
||||||
'notification': notification,
|
'notification': notification,
|
||||||
'settings_url': build_absolute_uri(
|
'settings_url': mainreverse_absolute(
|
||||||
'control:user.settings.notifications',
|
'control:user.settings.notifications',
|
||||||
),
|
),
|
||||||
'disable_url': build_absolute_uri(
|
'disable_url': mainreverse_absolute(
|
||||||
'control:user.settings.notifications.off',
|
'control:user.settings.notifications.off',
|
||||||
kwargs={
|
kwargs={
|
||||||
'token': user.notifications_token,
|
'token': user.notifications_token,
|
||||||
|
|||||||
@@ -3506,7 +3506,7 @@ def signal_listener_issue_media(sender: Event, order: Order, **kwargs):
|
|||||||
from pretix.base.models import ReusableMedium
|
from pretix.base.models import ReusableMedium
|
||||||
|
|
||||||
for p in order.positions.all():
|
for p in order.positions.all():
|
||||||
if p.item.media_policy in (Item.MEDIA_POLICY_NEW, Item.MEDIA_POLICY_REUSE_OR_NEW):
|
if p.item.media_policy in (Item.MEDIA_POLICY_NEW, Item.MEDIA_POLICY_REUSE_OR_NEW, Item.MEDIA_POLICY_APPEND_OR_NEW):
|
||||||
mt = MEDIA_TYPES[p.item.media_type]
|
mt = MEDIA_TYPES[p.item.media_type]
|
||||||
if mt.medium_created_by_server and not p.linked_media.exists():
|
if mt.medium_created_by_server and not p.linked_media.exists():
|
||||||
rm = ReusableMedium.objects.create(
|
rm = ReusableMedium.objects.create(
|
||||||
|
|||||||
@@ -327,7 +327,7 @@ def get_best_name(position_or_address, parts=False):
|
|||||||
|
|
||||||
@receiver(register_text_placeholders, dispatch_uid="pretixbase_register_text_placeholders")
|
@receiver(register_text_placeholders, dispatch_uid="pretixbase_register_text_placeholders")
|
||||||
def base_placeholders(sender, **kwargs):
|
def base_placeholders(sender, **kwargs):
|
||||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
from pretix.multidomain.urlreverse import eventreverse_absolute
|
||||||
|
|
||||||
def _event_sample(event):
|
def _event_sample(event):
|
||||||
if event.has_subevents:
|
if event.has_subevents:
|
||||||
@@ -388,14 +388,14 @@ def base_placeholders(sender, **kwargs):
|
|||||||
lambda event: LazyDate(now() + timedelta(days=15))
|
lambda event: LazyDate(now() + timedelta(days=15))
|
||||||
),
|
),
|
||||||
SimpleFunctionalTextPlaceholder(
|
SimpleFunctionalTextPlaceholder(
|
||||||
'url', ['order', 'event'], lambda order, event: build_absolute_uri(
|
'url', ['order', 'event'], lambda order, event: eventreverse_absolute(
|
||||||
event,
|
event,
|
||||||
'presale:event.order.open', kwargs={
|
'presale:event.order.open', kwargs={
|
||||||
'order': order.code,
|
'order': order.code,
|
||||||
'secret': order.secret,
|
'secret': order.secret,
|
||||||
'hash': order.email_confirm_secret()
|
'hash': order.email_confirm_secret()
|
||||||
}
|
}
|
||||||
), lambda event: build_absolute_uri(
|
), lambda event: eventreverse_absolute(
|
||||||
event,
|
event,
|
||||||
'presale:event.order.open', kwargs={
|
'presale:event.order.open', kwargs={
|
||||||
'order': 'F8VVL',
|
'order': 'F8VVL',
|
||||||
@@ -406,7 +406,7 @@ def base_placeholders(sender, **kwargs):
|
|||||||
),
|
),
|
||||||
SimpleButtonPlaceholder(
|
SimpleButtonPlaceholder(
|
||||||
'url_button', ['order', 'event'],
|
'url_button', ['order', 'event'],
|
||||||
url_func=lambda order, event: build_absolute_uri(
|
url_func=lambda order, event: eventreverse_absolute(
|
||||||
event,
|
event,
|
||||||
'presale:event.order.open', kwargs={
|
'presale:event.order.open', kwargs={
|
||||||
'order': order.code,
|
'order': order.code,
|
||||||
@@ -415,7 +415,7 @@ def base_placeholders(sender, **kwargs):
|
|||||||
}
|
}
|
||||||
),
|
),
|
||||||
text_func=lambda order, event: _("View order details"),
|
text_func=lambda order, event: _("View order details"),
|
||||||
sample_url_func=lambda event: build_absolute_uri(
|
sample_url_func=lambda event: eventreverse_absolute(
|
||||||
event,
|
event,
|
||||||
'presale:event.order.open', kwargs={
|
'presale:event.order.open', kwargs={
|
||||||
'order': 'F8VVL',
|
'order': 'F8VVL',
|
||||||
@@ -426,13 +426,13 @@ def base_placeholders(sender, **kwargs):
|
|||||||
sample_text_func=lambda event: _("View order details"),
|
sample_text_func=lambda event: _("View order details"),
|
||||||
),
|
),
|
||||||
SimpleFunctionalTextPlaceholder(
|
SimpleFunctionalTextPlaceholder(
|
||||||
'url_info_change', ['order', 'event'], lambda order, event: build_absolute_uri(
|
'url_info_change', ['order', 'event'], lambda order, event: eventreverse_absolute(
|
||||||
event,
|
event,
|
||||||
'presale:event.order.modify', kwargs={
|
'presale:event.order.modify', kwargs={
|
||||||
'order': order.code,
|
'order': order.code,
|
||||||
'secret': order.secret,
|
'secret': order.secret,
|
||||||
}
|
}
|
||||||
), lambda event: build_absolute_uri(
|
), lambda event: eventreverse_absolute(
|
||||||
event,
|
event,
|
||||||
'presale:event.order.modify', kwargs={
|
'presale:event.order.modify', kwargs={
|
||||||
'order': 'F8VVL',
|
'order': 'F8VVL',
|
||||||
@@ -441,13 +441,13 @@ def base_placeholders(sender, **kwargs):
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
SimpleFunctionalTextPlaceholder(
|
SimpleFunctionalTextPlaceholder(
|
||||||
'url_products_change', ['order', 'event'], lambda order, event: build_absolute_uri(
|
'url_products_change', ['order', 'event'], lambda order, event: eventreverse_absolute(
|
||||||
event,
|
event,
|
||||||
'presale:event.order.change', kwargs={
|
'presale:event.order.change', kwargs={
|
||||||
'order': order.code,
|
'order': order.code,
|
||||||
'secret': order.secret,
|
'secret': order.secret,
|
||||||
}
|
}
|
||||||
), lambda event: build_absolute_uri(
|
), lambda event: eventreverse_absolute(
|
||||||
event,
|
event,
|
||||||
'presale:event.order.change', kwargs={
|
'presale:event.order.change', kwargs={
|
||||||
'order': 'F8VVL',
|
'order': 'F8VVL',
|
||||||
@@ -456,13 +456,13 @@ def base_placeholders(sender, **kwargs):
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
SimpleFunctionalTextPlaceholder(
|
SimpleFunctionalTextPlaceholder(
|
||||||
'url_cancel', ['order', 'event'], lambda order, event: build_absolute_uri(
|
'url_cancel', ['order', 'event'], lambda order, event: eventreverse_absolute(
|
||||||
event,
|
event,
|
||||||
'presale:event.order.cancel', kwargs={
|
'presale:event.order.cancel', kwargs={
|
||||||
'order': order.code,
|
'order': order.code,
|
||||||
'secret': order.secret,
|
'secret': order.secret,
|
||||||
}
|
}
|
||||||
), lambda event: build_absolute_uri(
|
), lambda event: eventreverse_absolute(
|
||||||
event,
|
event,
|
||||||
'presale:event.order.cancel', kwargs={
|
'presale:event.order.cancel', kwargs={
|
||||||
'order': 'F8VVL',
|
'order': 'F8VVL',
|
||||||
@@ -471,7 +471,7 @@ def base_placeholders(sender, **kwargs):
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
SimpleFunctionalTextPlaceholder(
|
SimpleFunctionalTextPlaceholder(
|
||||||
'url', ['event', 'position'], lambda event, position: build_absolute_uri(
|
'url', ['event', 'position'], lambda event, position: eventreverse_absolute(
|
||||||
event,
|
event,
|
||||||
'presale:event.order.position',
|
'presale:event.order.position',
|
||||||
kwargs={
|
kwargs={
|
||||||
@@ -480,7 +480,7 @@ def base_placeholders(sender, **kwargs):
|
|||||||
'position': position.positionid
|
'position': position.positionid
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
lambda event: build_absolute_uri(
|
lambda event: eventreverse_absolute(
|
||||||
event,
|
event,
|
||||||
'presale:event.order.position', kwargs={
|
'presale:event.order.position', kwargs={
|
||||||
'order': 'F8VVL',
|
'order': 'F8VVL',
|
||||||
@@ -491,7 +491,7 @@ def base_placeholders(sender, **kwargs):
|
|||||||
),
|
),
|
||||||
SimpleButtonPlaceholder(
|
SimpleButtonPlaceholder(
|
||||||
'url_button', ['event', 'position'],
|
'url_button', ['event', 'position'],
|
||||||
url_func=lambda event, position: build_absolute_uri(
|
url_func=lambda event, position: eventreverse_absolute(
|
||||||
event,
|
event,
|
||||||
'presale:event.order.position', kwargs={
|
'presale:event.order.position', kwargs={
|
||||||
'order': position.order.code,
|
'order': position.order.code,
|
||||||
@@ -500,7 +500,7 @@ def base_placeholders(sender, **kwargs):
|
|||||||
}
|
}
|
||||||
),
|
),
|
||||||
text_func=lambda event, position: _("View registration details"),
|
text_func=lambda event, position: _("View registration details"),
|
||||||
sample_url_func=lambda event: build_absolute_uri(
|
sample_url_func=lambda event: eventreverse_absolute(
|
||||||
event,
|
event,
|
||||||
'presale:event.order.position', kwargs={
|
'presale:event.order.position', kwargs={
|
||||||
'order': 'F8VVL',
|
'order': 'F8VVL',
|
||||||
@@ -511,14 +511,14 @@ def base_placeholders(sender, **kwargs):
|
|||||||
sample_text_func=lambda event: _("View registration details"),
|
sample_text_func=lambda event: _("View registration details"),
|
||||||
),
|
),
|
||||||
SimpleFunctionalTextPlaceholder(
|
SimpleFunctionalTextPlaceholder(
|
||||||
'url_info_change', ['position', 'event'], lambda position, event: build_absolute_uri(
|
'url_info_change', ['position', 'event'], lambda position, event: eventreverse_absolute(
|
||||||
event,
|
event,
|
||||||
'presale:event.order.position.modify', kwargs={
|
'presale:event.order.position.modify', kwargs={
|
||||||
'order': position.order.code,
|
'order': position.order.code,
|
||||||
'secret': position.web_secret,
|
'secret': position.web_secret,
|
||||||
'position': position.positionid
|
'position': position.positionid
|
||||||
}
|
}
|
||||||
), lambda event: build_absolute_uri(
|
), lambda event: eventreverse_absolute(
|
||||||
event,
|
event,
|
||||||
'presale:event.order.position.modify', kwargs={
|
'presale:event.order.position.modify', kwargs={
|
||||||
'order': 'F8VVL',
|
'order': 'F8VVL',
|
||||||
@@ -528,14 +528,14 @@ def base_placeholders(sender, **kwargs):
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
SimpleFunctionalTextPlaceholder(
|
SimpleFunctionalTextPlaceholder(
|
||||||
'url_products_change', ['position', 'event'], lambda position, event: build_absolute_uri(
|
'url_products_change', ['position', 'event'], lambda position, event: eventreverse_absolute(
|
||||||
event,
|
event,
|
||||||
'presale:event.order.position.change', kwargs={
|
'presale:event.order.position.change', kwargs={
|
||||||
'order': position.order.code,
|
'order': position.order.code,
|
||||||
'secret': position.web_secret,
|
'secret': position.web_secret,
|
||||||
'position': position.positionid
|
'position': position.positionid
|
||||||
}
|
}
|
||||||
), lambda event: build_absolute_uri(
|
), lambda event: eventreverse_absolute(
|
||||||
event,
|
event,
|
||||||
'presale:event.order.position.change', kwargs={
|
'presale:event.order.position.change', kwargs={
|
||||||
'order': 'F8VVL',
|
'order': 'F8VVL',
|
||||||
@@ -581,20 +581,20 @@ def base_placeholders(sender, **kwargs):
|
|||||||
),
|
),
|
||||||
SimpleFunctionalTextPlaceholder(
|
SimpleFunctionalTextPlaceholder(
|
||||||
'url_remove', ['waiting_list_voucher', 'event'],
|
'url_remove', ['waiting_list_voucher', 'event'],
|
||||||
lambda waiting_list_voucher, event: build_absolute_uri(
|
lambda waiting_list_voucher, event: eventreverse_absolute(
|
||||||
event, 'presale:event.waitinglist.remove'
|
event, 'presale:event.waitinglist.remove'
|
||||||
) + '?voucher=' + waiting_list_voucher.code,
|
) + '?voucher=' + waiting_list_voucher.code,
|
||||||
lambda event: build_absolute_uri(
|
lambda event: eventreverse_absolute(
|
||||||
event,
|
event,
|
||||||
'presale:event.waitinglist.remove',
|
'presale:event.waitinglist.remove',
|
||||||
) + '?voucher=68CYU2H6ZTP3WLK5',
|
) + '?voucher=68CYU2H6ZTP3WLK5',
|
||||||
),
|
),
|
||||||
SimpleFunctionalTextPlaceholder(
|
SimpleFunctionalTextPlaceholder(
|
||||||
'url', ['waiting_list_voucher', 'event'],
|
'url', ['waiting_list_voucher', 'event'],
|
||||||
lambda waiting_list_voucher, event: build_absolute_uri(
|
lambda waiting_list_voucher, event: eventreverse_absolute(
|
||||||
event, 'presale:event.redeem'
|
event, 'presale:event.redeem'
|
||||||
) + '?voucher=' + waiting_list_voucher.code,
|
) + '?voucher=' + waiting_list_voucher.code,
|
||||||
lambda event: build_absolute_uri(
|
lambda event: eventreverse_absolute(
|
||||||
event,
|
event,
|
||||||
'presale:event.redeem',
|
'presale:event.redeem',
|
||||||
) + '?voucher=68CYU2H6ZTP3WLK5',
|
) + '?voucher=68CYU2H6ZTP3WLK5',
|
||||||
@@ -611,7 +611,7 @@ def base_placeholders(sender, **kwargs):
|
|||||||
'orders', ['event', 'orders'], lambda event, orders: '\n' + '\n\n'.join(
|
'orders', ['event', 'orders'], lambda event, orders: '\n' + '\n\n'.join(
|
||||||
'* {} - {}'.format(
|
'* {} - {}'.format(
|
||||||
order.full_code,
|
order.full_code,
|
||||||
build_absolute_uri(event, 'presale:event.order.open', kwargs={
|
eventreverse_absolute(event, 'presale:event.order.open', kwargs={
|
||||||
'event': event.slug,
|
'event': event.slug,
|
||||||
'organizer': event.organizer.slug,
|
'organizer': event.organizer.slug,
|
||||||
'order': order.code,
|
'order': order.code,
|
||||||
@@ -623,7 +623,7 @@ def base_placeholders(sender, **kwargs):
|
|||||||
), lambda event: '\n' + '\n\n'.join(
|
), lambda event: '\n' + '\n\n'.join(
|
||||||
'* {} - {}'.format(
|
'* {} - {}'.format(
|
||||||
'{}-{}'.format(event.slug.upper(), order['code']),
|
'{}-{}'.format(event.slug.upper(), order['code']),
|
||||||
build_absolute_uri(event, 'presale:event.order.open', kwargs={
|
eventreverse_absolute(event, 'presale:event.order.open', kwargs={
|
||||||
'event': event.slug,
|
'event': event.slug,
|
||||||
'organizer': event.organizer.slug,
|
'organizer': event.organizer.slug,
|
||||||
'order': order['code'],
|
'order': order['code'],
|
||||||
@@ -662,13 +662,13 @@ def base_placeholders(sender, **kwargs):
|
|||||||
# join vouchers with two spaces at end of line so markdown-parser inserts a <br>
|
# join vouchers with two spaces at end of line so markdown-parser inserts a <br>
|
||||||
'voucher_url_list', ['event', 'voucher_list'],
|
'voucher_url_list', ['event', 'voucher_list'],
|
||||||
lambda event, voucher_list: ' \n'.join([
|
lambda event, voucher_list: ' \n'.join([
|
||||||
build_absolute_uri(
|
eventreverse_absolute(
|
||||||
event, 'presale:event.redeem'
|
event, 'presale:event.redeem'
|
||||||
) + '?voucher=' + c
|
) + '?voucher=' + c
|
||||||
for c in voucher_list
|
for c in voucher_list
|
||||||
]),
|
]),
|
||||||
lambda event: ' \n'.join([
|
lambda event: ' \n'.join([
|
||||||
build_absolute_uri(
|
eventreverse_absolute(
|
||||||
event, 'presale:event.redeem'
|
event, 'presale:event.redeem'
|
||||||
) + '?voucher=' + c
|
) + '?voucher=' + c
|
||||||
for c in ['68CYU2H6ZTP3WLK5', '7MB94KKPVEPSMVF2']
|
for c in ['68CYU2H6ZTP3WLK5', '7MB94KKPVEPSMVF2']
|
||||||
@@ -676,10 +676,10 @@ def base_placeholders(sender, **kwargs):
|
|||||||
inline=False,
|
inline=False,
|
||||||
),
|
),
|
||||||
SimpleFunctionalTextPlaceholder(
|
SimpleFunctionalTextPlaceholder(
|
||||||
'url', ['event', 'voucher_list'], lambda event, voucher_list: build_absolute_uri(event, 'presale:event.index', kwargs={
|
'url', ['event', 'voucher_list'], lambda event, voucher_list: eventreverse_absolute(event, 'presale:event.index', kwargs={
|
||||||
'event': event.slug,
|
'event': event.slug,
|
||||||
'organizer': event.organizer.slug,
|
'organizer': event.organizer.slug,
|
||||||
}), lambda event: build_absolute_uri(event, 'presale:event.index', kwargs={
|
}), lambda event: eventreverse_absolute(event, 'presale:event.index', kwargs={
|
||||||
'event': event.slug,
|
'event': event.slug,
|
||||||
'organizer': event.organizer.slug,
|
'organizer': event.organizer.slug,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ from pretix.base.services.mail import mail
|
|||||||
from pretix.base.settings import GlobalSettingsObject
|
from pretix.base.settings import GlobalSettingsObject
|
||||||
from pretix.base.signals import periodic_task
|
from pretix.base.signals import periodic_task
|
||||||
from pretix.celery_app import app
|
from pretix.celery_app import app
|
||||||
from pretix.helpers.urls import build_absolute_uri
|
from pretix.helpers.urls import mainreverse_absolute
|
||||||
|
|
||||||
|
|
||||||
@receiver(signal=periodic_task)
|
@receiver(signal=periodic_task)
|
||||||
@@ -121,7 +121,7 @@ def send_update_notification_email():
|
|||||||
)
|
)
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
'url': build_absolute_uri('control:global.update')
|
'url': mainreverse_absolute('control:global.update')
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -211,12 +211,25 @@ DEFAULTS = {
|
|||||||
'form_class': forms.BooleanField,
|
'form_class': forms.BooleanField,
|
||||||
'serializer_class': serializers.BooleanField,
|
'serializer_class': serializers.BooleanField,
|
||||||
'form_kwargs': dict(
|
'form_kwargs': dict(
|
||||||
label=_("Activate re-usable media"),
|
label=_("Activate reusable media"),
|
||||||
help_text=_("The re-usable media feature allows you to connect tickets and gift cards with physical media "
|
help_text=_("The reusable media feature allows you to connect tickets and gift cards with physical media "
|
||||||
"such as wristbands or chip cards that may be re-used for different tickets or gift cards "
|
"such as wristbands or chip cards that may be reused for different tickets or gift cards "
|
||||||
"later.")
|
"later.")
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
'reusable_media_usage_enforced': {
|
||||||
|
'default': 'False',
|
||||||
|
'type': bool,
|
||||||
|
'form_class': forms.BooleanField,
|
||||||
|
'serializer_class': serializers.BooleanField,
|
||||||
|
'form_kwargs': dict(
|
||||||
|
label=_("Enforce the usage of issued reusable media for check-in"),
|
||||||
|
help_text=_("If enabled, a ticket barcode will not be accepted anymore, if a reusable medium has been "
|
||||||
|
"created and linked to a ticket. Keeping this option turned off will treat the reusable "
|
||||||
|
"medium and ticket as equals."),
|
||||||
|
widget=forms.CheckboxInput(attrs={'data-display-dependency': '#id_settings-reusable_media_active'}),
|
||||||
|
)
|
||||||
|
},
|
||||||
'reusable_media_type_barcode': {
|
'reusable_media_type_barcode': {
|
||||||
'default': 'False',
|
'default': 'False',
|
||||||
'type': bool,
|
'type': bool,
|
||||||
@@ -1911,8 +1924,6 @@ DEFAULTS = {
|
|||||||
'serializer_class': serializers.BooleanField,
|
'serializer_class': serializers.BooleanField,
|
||||||
'form_kwargs': dict(
|
'form_kwargs': dict(
|
||||||
label=_("Hide all past dates from calendar"),
|
label=_("Hide all past dates from calendar"),
|
||||||
help_text=_("This option currently only affects the calendar of this event series, not the organizer-wide "
|
|
||||||
"calendar.")
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
'allow_modifications': {
|
'allow_modifications': {
|
||||||
|
|||||||
@@ -535,8 +535,9 @@ EventPluginRegistry = PluginAwareRegistry # for backwards compatibility
|
|||||||
event_live_issues = EventPluginSignal()
|
event_live_issues = EventPluginSignal()
|
||||||
"""
|
"""
|
||||||
This signal is sent out to determine whether an event can be taken live. If you want to
|
This signal is sent out to determine whether an event can be taken live. If you want to
|
||||||
prevent the event from going live, return a string that will be displayed to the user
|
prevent the event from going live, return an error message to display to the user (either
|
||||||
as the error message. If you don't, your receiver should return ``None``.
|
as a SafeString containing HTML, or a string that will be HTML-escaped). If you don't,
|
||||||
|
your receiver should return ``None``.
|
||||||
|
|
||||||
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
|
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<link rel="icon" href="{% static "pretixbase/img/favicon.ico" %}">
|
<link rel="icon" href="{% static "pretixbase/img/favicon.ico" %}">
|
||||||
|
<script type="text/javascript" src="{% static "pretixbase/js/errors.js" %}"></script>
|
||||||
{% block custom_header %}{% endblock %}
|
{% block custom_header %}{% endblock %}
|
||||||
{% if css_theme %}
|
{% if css_theme %}
|
||||||
<link rel="stylesheet" type="text/css" href="{{ css_theme }}" />
|
<link rel="stylesheet" type="text/css" href="{{ css_theme }}" />
|
||||||
@@ -21,5 +22,4 @@
|
|||||||
{% block content %}{% endblock %}
|
{% block content %}{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
<script src="{% static "pretixbase/js/errors.js" %}"></script>
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
{% extends "error.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load eventurl %}
|
||||||
|
{% load urlreplace %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>{% trans "Please continue in a new tab" %}</h1>
|
||||||
|
<p class="larger">
|
||||||
|
{% blocktrans trimmed %}
|
||||||
|
For security reasons, the following step is only possible in a new tab.
|
||||||
|
{% endblocktrans %}
|
||||||
|
</p>
|
||||||
|
<p class="larger">
|
||||||
|
{% blocktrans trimmed %}
|
||||||
|
If the new tab did not open automatically, please click the following button:
|
||||||
|
{% endblocktrans %}
|
||||||
|
</p>
|
||||||
|
<div class="text-center">
|
||||||
|
<a href="{{ url }}"
|
||||||
|
class="btn btn-primary btn-lg" target="_blank">
|
||||||
|
<span class="fa fa-external-link-square"></span>
|
||||||
|
{% trans "Continue in new tab" %}
|
||||||
|
</a>
|
||||||
|
{{ url|json_script:"framebreak-url" }}
|
||||||
|
<script type="text/javascript" src="{% static "pretixbase/js/framebreak.js" %}"></script>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -2,13 +2,14 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load rich_text %}
|
{% load rich_text %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
{% load wrap_in %}
|
||||||
{% block title %}{% trans "Redirect" %}{% endblock %}
|
{% block title %}{% trans "Redirect" %}{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<i class="fa fa-link fa-fw big-icon"></i>
|
<i class="fa fa-link fa-fw big-icon"></i>
|
||||||
<div class="error-details">
|
<div class="error-details">
|
||||||
<h1>{% trans "Redirect" %}</h1>
|
<h1>{% trans "Redirect" %}</h1>
|
||||||
<h3>
|
<h3>
|
||||||
{% blocktrans trimmed with host="<strong>"|add:hostname|add:"</strong>"|safe %}
|
{% blocktrans trimmed with host=hostname|wrap_in:'strong' %}
|
||||||
The link you clicked on wants to redirect you to a destination on the website {{ host }}.
|
The link you clicked on wants to redirect you to a destination on the website {{ host }}.
|
||||||
{% endblocktrans %}
|
{% endblocktrans %}
|
||||||
{% blocktrans trimmed %}
|
{% blocktrans trimmed %}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
import importlib
|
import importlib
|
||||||
|
|
||||||
from django import template
|
from django import template
|
||||||
|
from django.utils.html import conditional_escape
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
from pretix.base.models import Event
|
from pretix.base.models import Event
|
||||||
@@ -44,7 +45,7 @@ def eventsignal(event: Event, signame: str, **kwargs):
|
|||||||
_html = []
|
_html = []
|
||||||
for receiver, response in signal.send(event, **kwargs):
|
for receiver, response in signal.send(event, **kwargs):
|
||||||
if response:
|
if response:
|
||||||
_html.append(response)
|
_html.append(conditional_escape(response))
|
||||||
return mark_safe("".join(_html))
|
return mark_safe("".join(_html))
|
||||||
|
|
||||||
|
|
||||||
@@ -63,5 +64,5 @@ def signal(signame: str, request, **kwargs):
|
|||||||
_html = []
|
_html = []
|
||||||
for receiver, response in signal.send(request, **kwargs):
|
for receiver, response in signal.send(request, **kwargs):
|
||||||
if response:
|
if response:
|
||||||
_html.append(response)
|
_html.append(conditional_escape(response))
|
||||||
return mark_safe("".join(_html))
|
return mark_safe("".join(_html))
|
||||||
|
|||||||
@@ -42,8 +42,6 @@ from bleach import DEFAULT_CALLBACKS, html5lib_shim
|
|||||||
from bleach.linkifier import build_email_re
|
from bleach.linkifier import build_email_re
|
||||||
from django import template
|
from django import template
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core import signing
|
|
||||||
from django.urls import reverse
|
|
||||||
from django.utils.functional import SimpleLazyObject
|
from django.utils.functional import SimpleLazyObject
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
from django.utils.http import url_has_allowed_host_and_scheme
|
from django.utils.http import url_has_allowed_host_and_scheme
|
||||||
@@ -54,6 +52,7 @@ from markdown.postprocessors import Postprocessor
|
|||||||
from markdown.treeprocessors import UnescapeTreeprocessor
|
from markdown.treeprocessors import UnescapeTreeprocessor
|
||||||
from tlds import tld_set
|
from tlds import tld_set
|
||||||
|
|
||||||
|
from pretix.base.views.redirect import safelink
|
||||||
from pretix.helpers.format import SafeFormatter, format_map
|
from pretix.helpers.format import SafeFormatter, format_map
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
@@ -158,8 +157,7 @@ def safelink_callback(attrs, new=False):
|
|||||||
"""
|
"""
|
||||||
url = html.unescape(attrs.get((None, 'href'), '/'))
|
url = html.unescape(attrs.get((None, 'href'), '/'))
|
||||||
if not url_has_allowed_host_and_scheme(url, allowed_hosts=None) and not url.startswith('mailto:') and not url.startswith('tel:'):
|
if not url_has_allowed_host_and_scheme(url, allowed_hosts=None) and not url.startswith('mailto:') and not url.startswith('tel:'):
|
||||||
signer = signing.Signer(salt='safe-redirect')
|
attrs[None, 'href'] = safelink(url)
|
||||||
attrs[None, 'href'] = reverse('redirect') + '?url=' + urllib.parse.quote(signer.sign(url))
|
|
||||||
attrs[None, 'target'] = '_blank'
|
attrs[None, 'target'] = '_blank'
|
||||||
attrs[None, 'rel'] = 'noopener'
|
attrs[None, 'rel'] = 'noopener'
|
||||||
return attrs
|
return attrs
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||||
# <https://www.gnu.org/licenses/>.
|
# <https://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
|
import logging
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
|
||||||
from django.core import signing
|
from django.core import signing
|
||||||
@@ -26,6 +27,8 @@ from django.http import HttpResponseBadRequest, HttpResponseRedirect
|
|||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def _is_samesite_referer(request):
|
def _is_samesite_referer(request):
|
||||||
referer = request.headers.get('referer')
|
referer = request.headers.get('referer')
|
||||||
@@ -42,11 +45,16 @@ def _is_samesite_referer(request):
|
|||||||
|
|
||||||
|
|
||||||
def redir_view(request):
|
def redir_view(request):
|
||||||
signer = signing.Signer(salt='safe-redirect')
|
framebreak = "framebreak" in request.GET
|
||||||
|
salt = 'framebreak-safelink-url' if framebreak else 'safelink-url'
|
||||||
try:
|
try:
|
||||||
url = signer.unsign(request.GET.get('url', ''))
|
url = signing.Signer(salt=salt).unsign(request.GET.get('url', ''))
|
||||||
except signing.BadSignature:
|
except signing.BadSignature:
|
||||||
return HttpResponseBadRequest('Invalid parameter')
|
try:
|
||||||
|
# Backwards-compatibility for a change in 2026-06, remove after a while
|
||||||
|
url = signing.Signer(salt='safe-redirect').unsign(request.GET.get('url', ''))
|
||||||
|
except signing.BadSignature:
|
||||||
|
return HttpResponseBadRequest('Invalid parameter')
|
||||||
|
|
||||||
if not _is_samesite_referer(request):
|
if not _is_samesite_referer(request):
|
||||||
u = urllib.parse.urlparse(url)
|
u = urllib.parse.urlparse(url)
|
||||||
@@ -55,11 +63,26 @@ def redir_view(request):
|
|||||||
'url': url,
|
'url': url,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if framebreak:
|
||||||
|
r = render(request, 'pretixbase/framebreak.html', {
|
||||||
|
'url': url,
|
||||||
|
})
|
||||||
|
r.xframe_options_exempt = True
|
||||||
|
return r
|
||||||
|
|
||||||
r = HttpResponseRedirect(url)
|
r = HttpResponseRedirect(url)
|
||||||
r['X-Robots-Tag'] = 'noindex'
|
r['X-Robots-Tag'] = 'noindex'
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
def safelink(url):
|
def safelink(url, framebreak=False):
|
||||||
signer = signing.Signer(salt='safe-redirect')
|
url = str(url)
|
||||||
return reverse('redirect') + '?url=' + urllib.parse.quote(signer.sign(url))
|
if not (url.startswith('https://') or url.startswith('http://') or url.startswith("/")):
|
||||||
|
logger.warning('Invalid URL passed to safelink: %r', url)
|
||||||
|
return '#invalid-url'
|
||||||
|
salt = 'framebreak-safelink-url' if framebreak else 'safelink-url'
|
||||||
|
signer = signing.Signer(salt=salt)
|
||||||
|
u = reverse('redirect') + '?url=' + urllib.parse.quote(signer.sign(url))
|
||||||
|
if framebreak:
|
||||||
|
u += "&framebreak=true"
|
||||||
|
return u
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ from pretix.control.forms.widgets import Select2
|
|||||||
from pretix.helpers.countries import CachedCountries
|
from pretix.helpers.countries import CachedCountries
|
||||||
from pretix.multidomain.models import AlternativeDomainAssignment, KnownDomain
|
from pretix.multidomain.models import AlternativeDomainAssignment, KnownDomain
|
||||||
from pretix.multidomain.urlreverse import (
|
from pretix.multidomain.urlreverse import (
|
||||||
build_absolute_uri, get_organizer_domain,
|
eventreverse_absolute, get_organizer_domain,
|
||||||
)
|
)
|
||||||
from pretix.plugins.banktransfer.payment import BankTransfer
|
from pretix.plugins.banktransfer.payment import BankTransfer
|
||||||
from pretix.presale.style import get_fonts
|
from pretix.presale.style import get_fonts
|
||||||
@@ -219,7 +219,7 @@ class EventWizardBasicsForm(I18nModelForm):
|
|||||||
self.fields['location'].widget.attrs['placeholder'] = _(
|
self.fields['location'].widget.attrs['placeholder'] = _(
|
||||||
'Sample Conference Center\nHeidelberg, Germany'
|
'Sample Conference Center\nHeidelberg, Germany'
|
||||||
)
|
)
|
||||||
self.fields['slug'].widget.prefix = build_absolute_uri(self.organizer, 'presale:organizer.index')
|
self.fields['slug'].widget.prefix = eventreverse_absolute(self.organizer, 'presale:organizer.index')
|
||||||
self.fields['tax_rate']._required = True # Do not render as optional because it is conditionally required
|
self.fields['tax_rate']._required = True # Do not render as optional because it is conditionally required
|
||||||
if self.has_subevents:
|
if self.has_subevents:
|
||||||
del self.fields['presale_start']
|
del self.fields['presale_start']
|
||||||
|
|||||||
@@ -1342,7 +1342,13 @@ class QuestionAnswerFilterForm(forms.Form):
|
|||||||
opqs = opqs.filter(canceled=False)
|
opqs = opqs.filter(canceled=False)
|
||||||
if fdata.get("item", "") != "":
|
if fdata.get("item", "") != "":
|
||||||
i = fdata.get("item", "")
|
i = fdata.get("item", "")
|
||||||
opqs = opqs.filter(item_id__in=(i,))
|
if '-' in i:
|
||||||
|
opqs = opqs.filter(
|
||||||
|
item_id=i.split('-')[0],
|
||||||
|
variation_id=i.split('-')[1],
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
opqs = opqs.filter(item_id=i)
|
||||||
|
|
||||||
return opqs
|
return opqs
|
||||||
|
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ from pretix.control.forms.event import (
|
|||||||
)
|
)
|
||||||
from pretix.control.forms.widgets import Select2, Select2Multiple
|
from pretix.control.forms.widgets import Select2, Select2Multiple
|
||||||
from pretix.multidomain.models import KnownDomain
|
from pretix.multidomain.models import KnownDomain
|
||||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
from pretix.multidomain.urlreverse import eventreverse_absolute
|
||||||
|
|
||||||
|
|
||||||
class OrganizerForm(I18nModelForm):
|
class OrganizerForm(I18nModelForm):
|
||||||
@@ -636,6 +636,7 @@ class OrganizerSettingsForm(SettingsForm):
|
|||||||
'cookie_consent_dialog_button_yes',
|
'cookie_consent_dialog_button_yes',
|
||||||
'cookie_consent_dialog_button_no',
|
'cookie_consent_dialog_button_no',
|
||||||
'reusable_media_active',
|
'reusable_media_active',
|
||||||
|
'reusable_media_usage_enforced',
|
||||||
'reusable_media_type_barcode',
|
'reusable_media_type_barcode',
|
||||||
'reusable_media_type_barcode_identifier_length',
|
'reusable_media_type_barcode_identifier_length',
|
||||||
'reusable_media_type_nfc_uid',
|
'reusable_media_type_nfc_uid',
|
||||||
@@ -790,7 +791,7 @@ class MailSettingsForm(SettingsForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
if 'url' in base_parameters:
|
if 'url' in base_parameters:
|
||||||
placeholders['url'] = build_absolute_uri(
|
placeholders['url'] = eventreverse_absolute(
|
||||||
self.organizer,
|
self.organizer,
|
||||||
'presale:organizer.customer.activate'
|
'presale:organizer.customer.activate'
|
||||||
) + '?token=' + get_random_string(30)
|
) + '?token=' + get_random_string(30)
|
||||||
|
|||||||
@@ -746,6 +746,7 @@ class CoreUserImpersonatedLogEntryType(UserImpersonatedLogEntryType):
|
|||||||
'pretix.reusable_medium.linked_orderposition.added': _('A new ticket has been added to the medium.'),
|
'pretix.reusable_medium.linked_orderposition.added': _('A new ticket has been added to the medium.'),
|
||||||
'pretix.reusable_medium.linked_orderposition.removed': _('A ticket has been removed from the medium.'),
|
'pretix.reusable_medium.linked_orderposition.removed': _('A ticket has been removed from the medium.'),
|
||||||
'pretix.reusable_medium.linked_orderposition.changed': _('The medium has been connected to a new ticket.'),
|
'pretix.reusable_medium.linked_orderposition.changed': _('The medium has been connected to a new ticket.'),
|
||||||
|
'pretix.reusable_medium.exchanged': _('The ticket #{positionid} was exchanged for reusable medium {medium_identifier}.'),
|
||||||
'pretix.reusable_medium.linked_giftcard.changed': _('The medium has been connected to a new gift card.'),
|
'pretix.reusable_medium.linked_giftcard.changed': _('The medium has been connected to a new gift card.'),
|
||||||
'pretix.email.error': _('Sending of an email has failed.'),
|
'pretix.email.error': _('Sending of an email has failed.'),
|
||||||
'pretix.event.comment': _('The event\'s internal comment has been updated.'),
|
'pretix.event.comment': _('The event\'s internal comment has been updated.'),
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ from urllib.parse import quote, urljoin, urlparse
|
|||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth import REDIRECT_FIELD_NAME, logout
|
from django.contrib.auth import REDIRECT_FIELD_NAME, logout
|
||||||
|
from django.contrib.auth.views import redirect_to_login
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.shortcuts import get_object_or_404, resolve_url
|
from django.shortcuts import get_object_or_404, resolve_url
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
@@ -212,14 +213,17 @@ class AuditLogMiddleware:
|
|||||||
if request.path.startswith(get_script_prefix() + 'control') and request.user.is_authenticated:
|
if request.path.startswith(get_script_prefix() + 'control') and request.user.is_authenticated:
|
||||||
if getattr(request.user, "is_hijacked", False):
|
if getattr(request.user, "is_hijacked", False):
|
||||||
hijack_history = request.session.get('hijack_history', False)
|
hijack_history = request.session.get('hijack_history', False)
|
||||||
hijacker = get_object_or_404(User, pk=hijack_history[0])
|
hijacker = get_object_or_404(User, pk=hijack_history[0]["user"])
|
||||||
ss = hijacker.get_active_staff_session(request.session.get('hijacker_session'))
|
ss = hijacker.get_active_staff_session(request.session.get('hijacker_session'))
|
||||||
if ss:
|
if not ss:
|
||||||
ss.logs.create(
|
# Staff session expired or not found
|
||||||
url=request.path,
|
logout(request)
|
||||||
method=request.method,
|
return redirect_to_login(request.get_full_path())
|
||||||
impersonating=request.user
|
ss.logs.create(
|
||||||
)
|
url=request.path,
|
||||||
|
method=request.method,
|
||||||
|
impersonating=request.user
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
ss = request.user.get_active_staff_session(request.session.session_key)
|
ss = request.user.get_active_staff_session(request.session.session_key)
|
||||||
if ss:
|
if ss:
|
||||||
|
|||||||
@@ -39,7 +39,8 @@ from pretix.base.signals import (
|
|||||||
html_page_start = GlobalSignal()
|
html_page_start = GlobalSignal()
|
||||||
"""
|
"""
|
||||||
This signal allows you to put code in the beginning of the main page for every
|
This signal allows you to put code in the beginning of the main page for every
|
||||||
page in the backend. You are expected to return HTML.
|
page in the backend. You are expected to return a SafeString containing HTML, or
|
||||||
|
a string that will be HTML-escaped.
|
||||||
|
|
||||||
The ``sender`` keyword argument will contain the request.
|
The ``sender`` keyword argument will contain the request.
|
||||||
"""
|
"""
|
||||||
@@ -129,7 +130,7 @@ event_dashboard_top = EventPluginSignal()
|
|||||||
Arguments: 'request'
|
Arguments: 'request'
|
||||||
|
|
||||||
This signal is sent out to include custom HTML in the top part of the the event dashboard.
|
This signal is sent out to include custom HTML in the top part of the the event dashboard.
|
||||||
Receivers should return HTML.
|
Receivers should return a SafeString containing HTML, or a string that will be HTML-escaped.
|
||||||
|
|
||||||
As with all event plugin signals, the ``sender`` keyword argument will contain the event.
|
As with all event plugin signals, the ``sender`` keyword argument will contain the event.
|
||||||
An additional keyword argument ``subevent`` *can* contain a sub-event.
|
An additional keyword argument ``subevent`` *can* contain a sub-event.
|
||||||
@@ -172,6 +173,7 @@ Arguments: 'form'
|
|||||||
|
|
||||||
This signal allows you to add additional HTML to the form that is used for modifying vouchers.
|
This signal allows you to add additional HTML to the form that is used for modifying vouchers.
|
||||||
You receive the form object in the ``form`` keyword argument.
|
You receive the form object in the ``form`` keyword argument.
|
||||||
|
Receivers should return a SafeString containing HTML, or a string that will be HTML-escaped.
|
||||||
|
|
||||||
As with all event plugin signals, the ``sender`` keyword argument will contain the event.
|
As with all event plugin signals, the ``sender`` keyword argument will contain the event.
|
||||||
"""
|
"""
|
||||||
@@ -209,6 +211,7 @@ Arguments: 'quota'
|
|||||||
|
|
||||||
This signal allows you to append HTML to a Quota's detail view. You receive the
|
This signal allows you to append HTML to a Quota's detail view. You receive the
|
||||||
quota as argument in the ``quota`` keyword argument.
|
quota as argument in the ``quota`` keyword argument.
|
||||||
|
Receivers should return a SafeString containing HTML, or a string that will be HTML-escaped.
|
||||||
|
|
||||||
As with all event plugin signals, the ``sender`` keyword argument will contain the event.
|
As with all event plugin signals, the ``sender`` keyword argument will contain the event.
|
||||||
"""
|
"""
|
||||||
@@ -219,6 +222,7 @@ Arguments: 'subevent'
|
|||||||
|
|
||||||
This signal allows you to append HTML to a SubEvent's detail view. You receive the
|
This signal allows you to append HTML to a SubEvent's detail view. You receive the
|
||||||
subevent as argument in the ``subevent`` keyword argument.
|
subevent as argument in the ``subevent`` keyword argument.
|
||||||
|
Receivers should return a SafeString containing HTML, or a string that will be HTML-escaped.
|
||||||
|
|
||||||
As with all event plugin signals, the ``sender`` keyword argument will contain the event.
|
As with all event plugin signals, the ``sender`` keyword argument will contain the event.
|
||||||
"""
|
"""
|
||||||
@@ -265,7 +269,8 @@ order_info = EventPluginSignal()
|
|||||||
"""
|
"""
|
||||||
Arguments: ``order``, ``request``
|
Arguments: ``order``, ``request``
|
||||||
|
|
||||||
This signal is sent out to display additional information on the order detail page
|
This signal is sent out to display additional information on the order detail page.
|
||||||
|
Receivers should return a SafeString containing HTML, or a string that will be HTML-escaped.
|
||||||
|
|
||||||
As with all event plugin signals, the ``sender`` keyword argument will contain the event.
|
As with all event plugin signals, the ``sender`` keyword argument will contain the event.
|
||||||
Additionally, the argument ``order`` and ``request`` are available.
|
Additionally, the argument ``order`` and ``request`` are available.
|
||||||
@@ -275,7 +280,8 @@ order_approve_info = EventPluginSignal()
|
|||||||
"""
|
"""
|
||||||
Arguments: ``order``, ``request``
|
Arguments: ``order``, ``request``
|
||||||
|
|
||||||
This signal is sent out to display additional information on the order approve page
|
This signal is sent out to display additional information on the order approve page.
|
||||||
|
Receivers should return a SafeString containing HTML, or a string that will be HTML-escaped.
|
||||||
|
|
||||||
As with all event plugin signals, the ``sender`` keyword argument will contain the event.
|
As with all event plugin signals, the ``sender`` keyword argument will contain the event.
|
||||||
Additionally, the argument ``order`` and ``request`` are available.
|
Additionally, the argument ``order`` and ``request`` are available.
|
||||||
@@ -286,6 +292,7 @@ order_position_buttons = EventPluginSignal()
|
|||||||
Arguments: ``order``, ``position``, ``request``
|
Arguments: ``order``, ``position``, ``request``
|
||||||
|
|
||||||
This signal is sent out to display additional buttons for a single position of an order.
|
This signal is sent out to display additional buttons for a single position of an order.
|
||||||
|
Receivers should return a SafeString containing HTML, or a string that will be HTML-escaped.
|
||||||
|
|
||||||
As with all event plugin signals, the ``sender`` keyword argument will contain the event.
|
As with all event plugin signals, the ``sender`` keyword argument will contain the event.
|
||||||
Additionally, the argument ``order`` and ``request`` are available.
|
Additionally, the argument ``order`` and ``request`` are available.
|
||||||
@@ -315,6 +322,7 @@ Arguments: 'request'
|
|||||||
|
|
||||||
This signal is sent out to include template snippets on the settings page of an event
|
This signal is sent out to include template snippets on the settings page of an event
|
||||||
that allows generating a pretix Widget code.
|
that allows generating a pretix Widget code.
|
||||||
|
Receivers should return a SafeString containing HTML, or a string that will be HTML-escaped.
|
||||||
|
|
||||||
As with all event plugin signals, the ``sender`` keyword argument will contain the event.
|
As with all event plugin signals, the ``sender`` keyword argument will contain the event.
|
||||||
A second keyword argument ``request`` will contain the request object.
|
A second keyword argument ``request`` will contain the request object.
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ from pretix import __version__
|
|||||||
from pretix.base.models import Order, OrderPayment, Transaction
|
from pretix.base.models import Order, OrderPayment, Transaction
|
||||||
from pretix.base.plugins import get_all_plugins
|
from pretix.base.plugins import get_all_plugins
|
||||||
from pretix.base.templatetags.money import money_filter
|
from pretix.base.templatetags.money import money_filter
|
||||||
|
from pretix.helpers.reportlab import PlainTextParagraph
|
||||||
from pretix.plugins.reports.exporters import ReportlabExportMixin
|
from pretix.plugins.reports.exporters import ReportlabExportMixin
|
||||||
from pretix.settings import DATA_DIR
|
from pretix.settings import DATA_DIR
|
||||||
|
|
||||||
@@ -79,23 +80,23 @@ class SysReport(ReportlabExportMixin):
|
|||||||
style_small.fontSize = 6
|
style_small.fontSize = 6
|
||||||
|
|
||||||
story = [
|
story = [
|
||||||
Paragraph("System report", headlinestyle),
|
PlainTextParagraph("System report", headlinestyle),
|
||||||
Spacer(1, 5 * mm),
|
Spacer(1, 5 * mm),
|
||||||
Paragraph("Usage", subheadlinestyle),
|
PlainTextParagraph("Usage", subheadlinestyle),
|
||||||
Spacer(1, 5 * mm),
|
Spacer(1, 5 * mm),
|
||||||
self._usage_table(),
|
self._usage_table(),
|
||||||
Spacer(1, 5 * mm),
|
Spacer(1, 5 * mm),
|
||||||
Paragraph("Installed versions", subheadlinestyle),
|
PlainTextParagraph("Installed versions", subheadlinestyle),
|
||||||
Spacer(1, 5 * mm),
|
Spacer(1, 5 * mm),
|
||||||
self._tech_table(),
|
self._tech_table(),
|
||||||
Spacer(1, 5 * mm),
|
Spacer(1, 5 * mm),
|
||||||
Paragraph("Plugins", subheadlinestyle),
|
PlainTextParagraph("Plugins", subheadlinestyle),
|
||||||
Spacer(1, 5 * mm),
|
Spacer(1, 5 * mm),
|
||||||
Paragraph(self._get_plugin_versions(), style_small),
|
PlainTextParagraph(self._get_plugin_versions(), style_small),
|
||||||
Spacer(1, 5 * mm),
|
Spacer(1, 5 * mm),
|
||||||
Paragraph("Custom templates", subheadlinestyle),
|
PlainTextParagraph("Custom templates", subheadlinestyle),
|
||||||
Spacer(1, 5 * mm),
|
Spacer(1, 5 * mm),
|
||||||
Paragraph(self._get_custom_templates(), style_small),
|
PlainTextParagraph(self._get_custom_templates(), style_small),
|
||||||
Spacer(1, 5 * mm),
|
Spacer(1, 5 * mm),
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -121,13 +122,13 @@ class SysReport(ReportlabExportMixin):
|
|||||||
("RIGHTPADDING", (-1, 0), (-1, -1), 0),
|
("RIGHTPADDING", (-1, 0), (-1, -1), 0),
|
||||||
]
|
]
|
||||||
tdata = [
|
tdata = [
|
||||||
[Paragraph("Site URL:", style), Paragraph(settings.SITE_URL, style)],
|
[PlainTextParagraph("Site URL:", style), Paragraph(settings.SITE_URL, style)],
|
||||||
[Paragraph("pretix version:", style), Paragraph(__version__, style)],
|
[PlainTextParagraph("pretix version:", style), Paragraph(__version__, style)],
|
||||||
[Paragraph("Python version:", style), Paragraph(sys.version, style)],
|
[PlainTextParagraph("Python version:", style), Paragraph(sys.version, style)],
|
||||||
[Paragraph("Platform:", style), Paragraph(platform.platform(), style)],
|
[PlainTextParagraph("Platform:", style), Paragraph(platform.platform(), style)],
|
||||||
[
|
[
|
||||||
Paragraph("Database engine:", style),
|
PlainTextParagraph("Database engine:", style),
|
||||||
Paragraph(settings.DATABASES["default"]["ENGINE"], style),
|
PlainTextParagraph(settings.DATABASES["default"]["ENGINE"], style),
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
table = Table(tdata, colWidths=colwidths, repeatRows=0)
|
table = Table(tdata, colWidths=colwidths, repeatRows=0)
|
||||||
@@ -206,7 +207,7 @@ class SysReport(ReportlabExportMixin):
|
|||||||
year_last = now().year
|
year_last = now().year
|
||||||
tdata = [
|
tdata = [
|
||||||
[
|
[
|
||||||
Paragraph(l, style_small_head)
|
PlainTextParagraph(l, style_small_head)
|
||||||
for l in (
|
for l in (
|
||||||
"Time frame",
|
"Time frame",
|
||||||
"Currency",
|
"Currency",
|
||||||
@@ -257,19 +258,19 @@ class SysReport(ReportlabExportMixin):
|
|||||||
|
|
||||||
tdata.append(
|
tdata.append(
|
||||||
(
|
(
|
||||||
Paragraph(
|
PlainTextParagraph(
|
||||||
date_format(first_day, "M Y")
|
date_format(first_day, "M Y")
|
||||||
+ " – "
|
+ " – "
|
||||||
+ date_format(after_day - timedelta(days=1), "M Y"),
|
+ date_format(after_day - timedelta(days=1), "M Y"),
|
||||||
style_small,
|
style_small,
|
||||||
),
|
),
|
||||||
Paragraph(c, style_small),
|
PlainTextParagraph(c, style_small),
|
||||||
Paragraph(str(orders_count), style_small) if i == 0 else "",
|
PlainTextParagraph(str(orders_count), style_small) if i == 0 else "",
|
||||||
Paragraph(money_filter(revenue_data.get("s_net") or 0, c), style_small),
|
PlainTextParagraph(money_filter(revenue_data.get("s_net") or 0, c), style_small),
|
||||||
Paragraph(str(testmode_count), style_small) if i == 0 else "",
|
PlainTextParagraph(str(testmode_count), style_small) if i == 0 else "",
|
||||||
Paragraph(str(unconfirmed_count), style_small) if i == 0 else "",
|
PlainTextParagraph(str(unconfirmed_count), style_small) if i == 0 else "",
|
||||||
Paragraph(str(revenue_data.get("c") or 0), style_small),
|
PlainTextParagraph(str(revenue_data.get("c") or 0), style_small),
|
||||||
Paragraph(money_filter(revenue_data.get("s_gross") or 0, c), style_small),
|
PlainTextParagraph(money_filter(revenue_data.get("s_gross") or 0, c), style_small),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -66,6 +66,7 @@
|
|||||||
<script type="text/javascript" src="{% static "lightbox/js/lightbox.js" %}"></script>
|
<script type="text/javascript" src="{% static "lightbox/js/lightbox.js" %}"></script>
|
||||||
<script type="text/javascript" src="{% static "are-you-sure/jquery.are-you-sure.js" %}"></script>
|
<script type="text/javascript" src="{% static "are-you-sure/jquery.are-you-sure.js" %}"></script>
|
||||||
<script type="text/javascript" src="{% static "pretixbase/js/addressform.js" %}"></script>
|
<script type="text/javascript" src="{% static "pretixbase/js/addressform.js" %}"></script>
|
||||||
|
<script type="text/javascript" src="{% static "pretixbase/js/errors.js" %}"></script>
|
||||||
{% endcompress %}
|
{% endcompress %}
|
||||||
{{ html_head|safe }}
|
{{ html_head|safe }}
|
||||||
|
|
||||||
|
|||||||
@@ -54,6 +54,8 @@
|
|||||||
<span class="fa fa-check-circle"></span>
|
<span class="fa fa-check-circle"></span>
|
||||||
{% elif result.status == "incomplete" %}
|
{% elif result.status == "incomplete" %}
|
||||||
<span class="fa fa-question-circle"></span>
|
<span class="fa fa-question-circle"></span>
|
||||||
|
{% elif result.status == "exchange" %}
|
||||||
|
<span class="fa fa-recycle"></span>
|
||||||
{% elif result.status == "error" %}
|
{% elif result.status == "error" %}
|
||||||
{% if result.reason == "already_redeemed" %}
|
{% if result.reason == "already_redeemed" %}
|
||||||
<span class="fa fa-warning"></span>
|
<span class="fa fa-warning"></span>
|
||||||
@@ -79,6 +81,14 @@
|
|||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
{% elif result.status == "exchange" %}
|
||||||
|
<h3 class="nomargin-top">{% trans "Media exchange required" %}</h3>
|
||||||
|
<p>
|
||||||
|
{% blocktrans trimmed with media_policy=media_policies|getitem:result.media_policy media_type=media_types|getitem:result.media_type %}
|
||||||
|
This ticket needs to be exchanged into a <strong>{{ media_type }}</strong> reusable medium.
|
||||||
|
<strong>{{ media_policy }}</strong>.
|
||||||
|
{% endblocktrans %}
|
||||||
|
</p>
|
||||||
{% elif result.status == "error" %}
|
{% elif result.status == "error" %}
|
||||||
<h3 class="nomargin-top">{{ reason_labels|getitem:result.reason }}</h3>
|
<h3 class="nomargin-top">{{ reason_labels|getitem:result.reason }}</h3>
|
||||||
{% if result.reason_explanation %}
|
{% if result.reason_explanation %}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
{% for issue in issues %}
|
{% for issue in issues %}
|
||||||
<li>{{ issue|safe }}</li>
|
<li>{{ issue }}</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
{% for issue in issues %}
|
{% for issue in issues %}
|
||||||
<li>{{ issue|safe }}</li>
|
<li>{{ issue }}</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -222,6 +222,7 @@
|
|||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>{% trans "Reusable media" %}</legend>
|
<legend>{% trans "Reusable media" %}</legend>
|
||||||
{% bootstrap_field sform.reusable_media_active layout="control" %}
|
{% bootstrap_field sform.reusable_media_active layout="control" %}
|
||||||
|
{% bootstrap_field sform.reusable_media_usage_enforced layout="control" %}
|
||||||
<div data-display-dependency="#{{ sform.reusable_media_active.id_for_label }}">
|
<div data-display-dependency="#{{ sform.reusable_media_active.id_for_label }}">
|
||||||
|
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
{% load getitem %}
|
{% load getitem %}
|
||||||
|
{% load icon %}
|
||||||
{% block inner %}
|
{% block inner %}
|
||||||
{% if team %}
|
{% if team %}
|
||||||
<h1>{% trans "Team:" %} {{ team.name }}</h1>
|
<h1>{% trans "Team:" %} {{ team.name }}</h1>
|
||||||
@@ -25,6 +26,18 @@
|
|||||||
<legend>{% trans "Organizer permissions" %}</legend>
|
<legend>{% trans "Organizer permissions" %}</legend>
|
||||||
{% bootstrap_field form.all_organizer_permissions layout="control" %}
|
{% bootstrap_field form.all_organizer_permissions layout="control" %}
|
||||||
<div class="team-permission-groups col-md-9 col-md-offset-3" data-display-dependency="#id_all_organizer_permissions" data-inverse>
|
<div class="team-permission-groups col-md-9 col-md-offset-3" data-display-dependency="#id_all_organizer_permissions" data-inverse>
|
||||||
|
<p class="text-muted">
|
||||||
|
{% icon "info-circle" %}
|
||||||
|
{% blocktrans trimmed %}
|
||||||
|
Even if a team has no access to a certain category of data, they might still be able to see
|
||||||
|
parts of this data when it is linked to data they can see.
|
||||||
|
{% endblocktrans %}
|
||||||
|
{% blocktrans trimmed %}
|
||||||
|
For example, someone with access to customer accounts will be able to see some information
|
||||||
|
about gift cards linked to a customer account, even if they generally can't see gift cards
|
||||||
|
directly.
|
||||||
|
{% endblocktrans %}
|
||||||
|
</p>
|
||||||
{% for f in form.organizer_field_names %}
|
{% for f in form.organizer_field_names %}
|
||||||
{% bootstrap_field form|getitem:f layout="control" %}
|
{% bootstrap_field form|getitem:f layout="control" %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@@ -37,6 +50,17 @@
|
|||||||
{% bootstrap_field form.limit_events layout="control" %}
|
{% bootstrap_field form.limit_events layout="control" %}
|
||||||
{% bootstrap_field form.all_event_permissions layout="control" %}
|
{% bootstrap_field form.all_event_permissions layout="control" %}
|
||||||
<div class="team-permission-groups col-md-9 col-md-offset-3" data-display-dependency="#id_all_event_permissions" data-inverse>
|
<div class="team-permission-groups col-md-9 col-md-offset-3" data-display-dependency="#id_all_event_permissions" data-inverse>
|
||||||
|
<p class="text-muted">
|
||||||
|
{% icon "info-circle" %}
|
||||||
|
{% blocktrans trimmed %}
|
||||||
|
Even if a team has no access to a certain category of data, they might still be able to see
|
||||||
|
parts of this data when it is linked to data they can see.
|
||||||
|
{% endblocktrans %}
|
||||||
|
{% blocktrans trimmed %}
|
||||||
|
For example, someone with access to orders will be able to see some information about
|
||||||
|
vouchers used to create an order, even if they generally can't see vouchers directly.
|
||||||
|
{% endblocktrans %}
|
||||||
|
</p>
|
||||||
{% for f in form.event_field_names %}
|
{% for f in form.event_field_names %}
|
||||||
{% bootstrap_field form|getitem:f layout="control" %}
|
{% bootstrap_field form|getitem:f layout="control" %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -19,9 +19,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<script type="application/json" id="editor-data">
|
{{ layout|json_script:"editor-data" }}
|
||||||
{{ layout|safe }}
|
|
||||||
</script>
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
<div class="panel panel-default panel-pdf-editor">
|
<div class="panel panel-default panel-pdf-editor">
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ from i18nfield.strings import LazyI18nString
|
|||||||
|
|
||||||
from pretix.api.views.checkin import _redeem_process
|
from pretix.api.views.checkin import _redeem_process
|
||||||
from pretix.base.media import MEDIA_TYPES
|
from pretix.base.media import MEDIA_TYPES
|
||||||
from pretix.base.models import Checkin, LogEntry, Order, OrderPosition
|
from pretix.base.models import Checkin, Item, LogEntry, Order, OrderPosition
|
||||||
from pretix.base.models.checkin import CheckinList
|
from pretix.base.models.checkin import CheckinList
|
||||||
from pretix.base.models.orders import PrintLog
|
from pretix.base.models.orders import PrintLog
|
||||||
from pretix.base.permissions import AnyPermissionOf
|
from pretix.base.permissions import AnyPermissionOf
|
||||||
@@ -533,6 +533,8 @@ class CheckInListSimulator(EventPermissionRequiredMixin, FormView):
|
|||||||
checkinlist=self.list,
|
checkinlist=self.list,
|
||||||
result=self.result,
|
result=self.result,
|
||||||
reason_labels=dict(Checkin.REASONS),
|
reason_labels=dict(Checkin.REASONS),
|
||||||
|
media_policies=dict(Item.MEDIA_POLICIES),
|
||||||
|
media_types=dict(MEDIA_TYPES),
|
||||||
)
|
)
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ from collections import OrderedDict, defaultdict
|
|||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from itertools import groupby
|
from itertools import groupby
|
||||||
from urllib.parse import urlparse, urlsplit
|
from urllib.parse import urlsplit
|
||||||
from zoneinfo import ZoneInfo
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
import bleach
|
import bleach
|
||||||
@@ -64,7 +64,6 @@ from django.shortcuts import get_object_or_404, redirect
|
|||||||
from django.urls import NoReverseMatch, reverse
|
from django.urls import NoReverseMatch, reverse
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils.html import conditional_escape, format_html
|
from django.utils.html import conditional_escape, format_html
|
||||||
from django.utils.http import url_has_allowed_host_and_scheme
|
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.utils.translation import gettext, gettext_lazy as _, gettext_noop
|
from django.utils.translation import gettext, gettext_lazy as _, gettext_noop
|
||||||
@@ -97,7 +96,9 @@ from pretix.control.permissions import EventPermissionRequiredMixin
|
|||||||
from pretix.control.views.mailsetup import MailSettingsSetupView
|
from pretix.control.views.mailsetup import MailSettingsSetupView
|
||||||
from pretix.control.views.user import RecentAuthenticationRequiredMixin
|
from pretix.control.views.user import RecentAuthenticationRequiredMixin
|
||||||
from pretix.helpers.database import rolledback_transaction
|
from pretix.helpers.database import rolledback_transaction
|
||||||
from pretix.multidomain.urlreverse import build_absolute_uri, get_event_domain
|
from pretix.multidomain.urlreverse import (
|
||||||
|
eventreverse_absolute, get_event_domain,
|
||||||
|
)
|
||||||
from pretix.presale.views.widget import (
|
from pretix.presale.views.widget import (
|
||||||
version_default as widget_version_default,
|
version_default as widget_version_default,
|
||||||
)
|
)
|
||||||
@@ -1148,8 +1149,11 @@ class EventLive(EventPermissionRequiredMixin, TemplateView):
|
|||||||
if request.POST.get("delete") == "yes":
|
if request.POST.get("delete") == "yes":
|
||||||
try:
|
try:
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
for order in request.event.orders.filter(testmode=True):
|
Order.gracefully_delete_bulk(
|
||||||
order.gracefully_delete(user=self.request.user)
|
request.event,
|
||||||
|
request.event.orders.filter(testmode=True),
|
||||||
|
user=self.request.user
|
||||||
|
)
|
||||||
except ProtectedError:
|
except ProtectedError:
|
||||||
messages.error(self.request, _('An order could not be deleted as some constraints (e.g. data '
|
messages.error(self.request, _('An order could not be deleted as some constraints (e.g. data '
|
||||||
'created by plug-ins) do not allow it.'))
|
'created by plug-ins) do not allow it.'))
|
||||||
@@ -1734,10 +1738,10 @@ class EventQRCode(EventPermissionRequiredMixin, View):
|
|||||||
permission = None
|
permission = None
|
||||||
|
|
||||||
def get(self, request, *args, filetype, **kwargs):
|
def get(self, request, *args, filetype, **kwargs):
|
||||||
url = build_absolute_uri(request.event, 'presale:event.index')
|
url = eventreverse_absolute(request.event, 'presale:event.index')
|
||||||
|
|
||||||
if "url" in request.GET:
|
if "url" in request.GET:
|
||||||
if url_has_allowed_host_and_scheme(request.GET["url"], allowed_hosts=[urlparse(url).netloc]):
|
if request.GET["url"].startswith(url):
|
||||||
url = request.GET["url"]
|
url = request.GET["url"]
|
||||||
else:
|
else:
|
||||||
raise PermissionDenied("Untrusted URL")
|
raise PermissionDenied("Untrusted URL")
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ from pretix.helpers import OF_SELF
|
|||||||
from pretix.helpers.compat import CompatDeleteView
|
from pretix.helpers.compat import CompatDeleteView
|
||||||
from pretix.helpers.format import SafeFormatter, format_map
|
from pretix.helpers.format import SafeFormatter, format_map
|
||||||
from pretix.helpers.hierarkey import clean_filename
|
from pretix.helpers.hierarkey import clean_filename
|
||||||
|
from pretix.helpers.iter import chunked_iterable
|
||||||
from pretix.helpers.json import CustomJSONEncoder
|
from pretix.helpers.json import CustomJSONEncoder
|
||||||
from pretix.helpers.safedownload import check_token
|
from pretix.helpers.safedownload import check_token
|
||||||
from pretix.presale.signals import question_form_fields
|
from pretix.presale.signals import question_form_fields
|
||||||
@@ -240,7 +241,7 @@ class BaseOrderBulkActionView(OrderSearchMixin, EventPermissionRequiredMixin, As
|
|||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def execute_bulk(self, queryset: QuerySet, form: forms.Form):
|
def execute_bulk(self, queryset: QuerySet, form: forms.Form):
|
||||||
qs = self.allowed_for(self.allowed_for(self.get_queryset()))
|
qs = self.allowed_for(self.get_queryset())
|
||||||
total = qs.count()
|
total = qs.count()
|
||||||
orders_with_successful_action = 0
|
orders_with_successful_action = 0
|
||||||
for i, o in enumerate(qs):
|
for i, o in enumerate(qs):
|
||||||
@@ -394,8 +395,21 @@ class OrderDeleteBulkActionView(BaseOrderBulkActionView):
|
|||||||
testmode=True,
|
testmode=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
def execute_single(self, instance, form: forms.Form):
|
def execute_bulk(self, queryset: QuerySet, form: forms.Form):
|
||||||
instance.gracefully_delete(user=self.request.user)
|
qs = self.allowed_for(self.get_queryset())
|
||||||
|
total = qs.count()
|
||||||
|
all_ids = list(qs.values_list("id", flat=True))
|
||||||
|
|
||||||
|
orders_with_successful_action = 0
|
||||||
|
for chunk in chunked_iterable(all_ids, 1000):
|
||||||
|
Order.gracefully_delete_bulk(
|
||||||
|
self.request.event,
|
||||||
|
qs.filter(id__in=chunk),
|
||||||
|
user=self.request.user,
|
||||||
|
)
|
||||||
|
orders_with_successful_action += len(chunk)
|
||||||
|
self.async_set_progress(orders_with_successful_action / total * 100)
|
||||||
|
return orders_with_successful_action, total
|
||||||
|
|
||||||
|
|
||||||
class OrderList(OrderSearchMixin, EventPermissionRequiredMixin, PaginationMixin, ListView):
|
class OrderList(OrderSearchMixin, EventPermissionRequiredMixin, PaginationMixin, ListView):
|
||||||
|
|||||||
@@ -139,8 +139,8 @@ from pretix.helpers import OF_SELF, GroupConcat
|
|||||||
from pretix.helpers.compat import CompatDeleteView
|
from pretix.helpers.compat import CompatDeleteView
|
||||||
from pretix.helpers.dicts import merge_dicts
|
from pretix.helpers.dicts import merge_dicts
|
||||||
from pretix.helpers.format import SafeFormatter, format_map
|
from pretix.helpers.format import SafeFormatter, format_map
|
||||||
from pretix.helpers.urls import build_absolute_uri as build_global_uri
|
from pretix.helpers.urls import mainreverse_absolute
|
||||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
from pretix.multidomain.urlreverse import eventreverse_absolute
|
||||||
from pretix.presale.forms.customer import TokenGenerator
|
from pretix.presale.forms.customer import TokenGenerator
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -1039,7 +1039,7 @@ class TeamMemberView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin,
|
|||||||
'user': self,
|
'user': self,
|
||||||
'organizer': self.request.organizer.name,
|
'organizer': self.request.organizer.name,
|
||||||
'team': instance.team.name,
|
'team': instance.team.name,
|
||||||
'url': build_global_uri('control:auth.invite', kwargs={
|
'url': mainreverse_absolute('control:auth.invite', kwargs={
|
||||||
'token': instance.token
|
'token': instance.token
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -2851,10 +2851,12 @@ class SSOProviderUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequire
|
|||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
ctx = super().get_context_data(**kwargs)
|
ctx = super().get_context_data(**kwargs)
|
||||||
ctx['redirect_uri'] = build_absolute_uri(self.request.organizer, 'presale:organizer.customer.login.return',
|
ctx['redirect_uri'] = eventreverse_absolute(
|
||||||
kwargs={
|
self.request.organizer, 'presale:organizer.customer.login.return',
|
||||||
'provider': self.object.pk
|
kwargs={
|
||||||
})
|
'provider': self.object.pk
|
||||||
|
}
|
||||||
|
)
|
||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
@@ -3085,7 +3087,7 @@ class CustomerDetailView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMi
|
|||||||
self.customer.log_action('pretix.customer.password.resetrequested', {}, user=self.request.user)
|
self.customer.log_action('pretix.customer.password.resetrequested', {}, user=self.request.user)
|
||||||
ctx = self.customer.get_email_context()
|
ctx = self.customer.get_email_context()
|
||||||
token = TokenGenerator().make_token(self.customer)
|
token = TokenGenerator().make_token(self.customer)
|
||||||
ctx['url'] = build_absolute_uri(
|
ctx['url'] = eventreverse_absolute(
|
||||||
self.request.organizer,
|
self.request.organizer,
|
||||||
'presale:organizer.customer.recoverpw'
|
'presale:organizer.customer.recoverpw'
|
||||||
) + '?id=' + self.customer.identifier + '&token=' + token
|
) + '?id=' + self.customer.identifier + '&token=' + token
|
||||||
|
|||||||
@@ -284,7 +284,7 @@ class BaseEditorView(EventPermissionRequiredMixin, TemplateView):
|
|||||||
ctx['pdf'] = self.get_current_background()
|
ctx['pdf'] = self.get_current_background()
|
||||||
ctx['variables'] = self.get_variables()
|
ctx['variables'] = self.get_variables()
|
||||||
ctx['images'] = self.get_images()
|
ctx['images'] = self.get_images()
|
||||||
ctx['layout'] = json.dumps(self.get_current_layout())
|
ctx['layout'] = self.get_current_layout()
|
||||||
ctx['title'] = self.title
|
ctx['title'] = self.title
|
||||||
ctx['locales'] = [p for p in settings.LANGUAGES if p[0] in self.request.event.settings.locales]
|
ctx['locales'] = [p for p in settings.LANGUAGES if p[0] in self.request.event.settings.locales]
|
||||||
ctx['maxfilesize'] = self.maxfilesize
|
ctx['maxfilesize'] = self.maxfilesize
|
||||||
|
|||||||
@@ -537,6 +537,7 @@ class SubEventDetail(EventPermissionRequiredMixin, DetailView):
|
|||||||
pcnt=Subquery(
|
pcnt=Subquery(
|
||||||
OrderPosition.objects.filter(
|
OrderPosition.objects.filter(
|
||||||
subevent=self.object,
|
subevent=self.object,
|
||||||
|
order_id=OuterRef("id"),
|
||||||
).values("subevent").annotate(c=Count("*")).values("c")
|
).values("subevent").annotate(c=Count("*")).values("c")
|
||||||
),
|
),
|
||||||
has_cancellation_request=Exists(CancellationRequest.objects.filter(order=OuterRef("pk"))),
|
has_cancellation_request=Exists(CancellationRequest.objects.filter(order=OuterRef("pk"))),
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ def giftcard_select2(request, **kwargs):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
page = 1
|
page = 1
|
||||||
|
|
||||||
if request.user.has_organizer_permission(request.organizer, 'organizer.giftcards:write', request):
|
if request.user.has_organizer_permission(request.organizer, 'organizer.giftcards:read', request):
|
||||||
qs = request.organizer.issued_gift_cards.filter(
|
qs = request.organizer.issued_gift_cards.filter(
|
||||||
Q(secret__icontains=query)
|
Q(secret__icontains=query)
|
||||||
).order_by('secret')
|
).order_by('secret')
|
||||||
|
|||||||
@@ -19,19 +19,23 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||||
# <https://www.gnu.org/licenses/>.
|
# <https://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
|
import hmac
|
||||||
import json
|
import json
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth import (
|
from django.contrib.auth import (
|
||||||
BACKEND_SESSION_KEY, get_user_model, load_backend, login,
|
BACKEND_SESSION_KEY, HASH_SESSION_KEY, get_user_model, load_backend, login,
|
||||||
|
logout,
|
||||||
)
|
)
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
from django.contrib.auth.views import redirect_to_login
|
||||||
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.shortcuts import get_object_or_404, redirect
|
from django.shortcuts import get_object_or_404, redirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.crypto import get_random_string
|
from django.utils.crypto import get_random_string, salted_hmac
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.views import View
|
from django.views import View
|
||||||
@@ -218,11 +222,13 @@ class UserImpersonateView(AdministratorPermissionRequiredMixin, RecentAuthentica
|
|||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
self.object = get_object_or_404(User, pk=self.kwargs.get("id"))
|
self.object = get_object_or_404(User, pk=self.kwargs.get("id"))
|
||||||
|
staff_session = request.user.get_active_staff_session(request.session.session_key)
|
||||||
self.request.user.log_action('pretix.control.auth.user.impersonated',
|
self.request.user.log_action('pretix.control.auth.user.impersonated',
|
||||||
user=request.user,
|
user=request.user,
|
||||||
data={
|
data={
|
||||||
'other': self.kwargs.get("id"),
|
'other': self.kwargs.get("id"),
|
||||||
'other_email': self.object.email
|
'other_email': self.object.email,
|
||||||
|
'staff_session': staff_session.pk,
|
||||||
})
|
})
|
||||||
oldkey = request.session.session_key
|
oldkey = request.session.session_key
|
||||||
|
|
||||||
@@ -230,7 +236,15 @@ class UserImpersonateView(AdministratorPermissionRequiredMixin, RecentAuthentica
|
|||||||
hijacked = self.object
|
hijacked = self.object
|
||||||
|
|
||||||
hijack_history = request.session.get("hijack_history", [])
|
hijack_history = request.session.get("hijack_history", [])
|
||||||
hijack_history.append(request.user._meta.pk.value_to_string(hijacker))
|
hijack_history.append({
|
||||||
|
"user": request.user.pk,
|
||||||
|
# We include the auth_hash, because it is unguessable. So should an attacker gain an attack vector to
|
||||||
|
# modify hijack_history, they can't just insert or change a user that shouldn't be there. We HMAC it
|
||||||
|
# again, though, since we also do not want the auth_hash of the admin user to be in the session of an
|
||||||
|
# unprivileged user to contain the risk if there is some leak of session data.
|
||||||
|
"auth_hash": salted_hmac(key_salt=b"hijack-history-hash", value=request.session[HASH_SESSION_KEY],
|
||||||
|
algorithm="sha256", secret=settings.SECRET_KEY).hexdigest(),
|
||||||
|
})
|
||||||
|
|
||||||
backend = get_used_backend(request)
|
backend = get_used_backend(request)
|
||||||
backend = f"{backend.__module__}.{backend.__class__.__name__}"
|
backend = f"{backend.__module__}.{backend.__class__.__name__}"
|
||||||
@@ -238,6 +252,12 @@ class UserImpersonateView(AdministratorPermissionRequiredMixin, RecentAuthentica
|
|||||||
with signals.no_update_last_login(), keep_session_age(request.session):
|
with signals.no_update_last_login(), keep_session_age(request.session):
|
||||||
login(request, hijacked, backend=backend)
|
login(request, hijacked, backend=backend)
|
||||||
|
|
||||||
|
request.session.save()
|
||||||
|
staff_session.logs.create(
|
||||||
|
method='(NOTE)',
|
||||||
|
url=f'Begin impersonating user #{hijacked.pk} (request session {oldkey[:8]} -> {request.session.session_key[:8]})',
|
||||||
|
)
|
||||||
|
|
||||||
request.session["hijack_history"] = hijack_history
|
request.session["hijack_history"] = hijack_history
|
||||||
|
|
||||||
signals.hijack_started.send(
|
signals.hijack_started.send(
|
||||||
@@ -254,13 +274,28 @@ class UserImpersonateView(AdministratorPermissionRequiredMixin, RecentAuthentica
|
|||||||
class UserImpersonateStopView(LoginRequiredMixin, View):
|
class UserImpersonateStopView(LoginRequiredMixin, View):
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
impersonated = request.user
|
staff_session_key = request.session['hijacker_session']
|
||||||
|
prev_session_key = request.session.session_key
|
||||||
hijs = request.session['hijacker_session']
|
|
||||||
hijack_history = request.session.get("hijack_history", [])
|
hijack_history = request.session.get("hijack_history", [])
|
||||||
hijacked = request.user
|
hijacked = request.user
|
||||||
user_pk = hijack_history.pop()
|
prev_session = hijack_history.pop()
|
||||||
hijacker = get_object_or_404(get_user_model(), pk=user_pk)
|
hijacker = get_object_or_404(get_user_model(), pk=prev_session["user"])
|
||||||
|
staff_session = hijacker.get_active_staff_session(staff_session_key)
|
||||||
|
if not staff_session:
|
||||||
|
raise PermissionDenied
|
||||||
|
|
||||||
|
expected_hash = salted_hmac(
|
||||||
|
key_salt=b"hijack-history-hash",
|
||||||
|
value=hijacker.get_session_auth_hash(),
|
||||||
|
algorithm="sha256",
|
||||||
|
secret=settings.SECRET_KEY
|
||||||
|
).hexdigest()
|
||||||
|
if not hmac.compare_digest(expected_hash, prev_session["auth_hash"]):
|
||||||
|
# Could be an attacker-controlled hijack history, but could also be e.g. a password change of the admin user
|
||||||
|
# that happened during the hijack session
|
||||||
|
logout(request)
|
||||||
|
return redirect_to_login(request.get_full_path())
|
||||||
|
|
||||||
backend = get_used_backend(request)
|
backend = get_used_backend(request)
|
||||||
backend = f"{backend.__module__}.{backend.__class__.__name__}"
|
backend = f"{backend.__module__}.{backend.__class__.__name__}"
|
||||||
with signals.no_update_last_login(), keep_session_age(request.session):
|
with signals.no_update_last_login(), keep_session_age(request.session):
|
||||||
@@ -275,17 +310,22 @@ class UserImpersonateStopView(LoginRequiredMixin, View):
|
|||||||
hijacked=hijacked,
|
hijacked=hijacked,
|
||||||
)
|
)
|
||||||
|
|
||||||
ss = request.user.get_active_staff_session(hijs)
|
request.session.save()
|
||||||
if ss:
|
staff_session.session_key = request.session.session_key
|
||||||
request.session.save()
|
staff_session.save()
|
||||||
ss.session_key = request.session.session_key
|
|
||||||
ss.save()
|
staff_session.logs.create(
|
||||||
|
method='(NOTE)',
|
||||||
|
url=f'Stop impersonating user #{hijacked.pk} (request session {prev_session_key[:8]}, '
|
||||||
|
f'staff session {staff_session_key[:8]} -> {request.session.session_key[:8]})',
|
||||||
|
)
|
||||||
|
|
||||||
request.user.log_action('pretix.control.auth.user.impersonate_stopped',
|
request.user.log_action('pretix.control.auth.user.impersonate_stopped',
|
||||||
user=request.user,
|
user=request.user,
|
||||||
data={
|
data={
|
||||||
'other': impersonated.pk,
|
'other': hijacked.pk,
|
||||||
'other_email': impersonated.email
|
'other_email': hijacked.email,
|
||||||
|
'staff_session': staff_session.pk,
|
||||||
})
|
})
|
||||||
return redirect(reverse('control:index'))
|
return redirect(reverse('control:index'))
|
||||||
|
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ from pretix.control.views import PaginationMixin
|
|||||||
from pretix.helpers.compat import CompatDeleteView
|
from pretix.helpers.compat import CompatDeleteView
|
||||||
from pretix.helpers.format import SafeFormatter, format_map
|
from pretix.helpers.format import SafeFormatter, format_map
|
||||||
from pretix.helpers.models import modelcopy
|
from pretix.helpers.models import modelcopy
|
||||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
from pretix.multidomain.urlreverse import eventreverse_absolute
|
||||||
|
|
||||||
|
|
||||||
class VoucherList(PaginationMixin, EventPermissionRequiredMixin, ListView):
|
class VoucherList(PaginationMixin, EventPermissionRequiredMixin, ListView):
|
||||||
@@ -338,7 +338,7 @@ class VoucherUpdate(EventPermissionRequiredMixin, UpdateView):
|
|||||||
}
|
}
|
||||||
if self.object.subevent_id:
|
if self.object.subevent_id:
|
||||||
url_params['subevent'] = self.object.subevent_id
|
url_params['subevent'] = self.object.subevent_id
|
||||||
ctx['url'] = build_absolute_uri(self.request.event, "presale:event.redeem") + "?" + urlencode(url_params)
|
ctx['url'] = eventreverse_absolute(self.request.event, "presale:event.redeem") + "?" + urlencode(url_params)
|
||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -29,3 +29,8 @@ class PretixHelpersConfig(AppConfig):
|
|||||||
def ready(self):
|
def ready(self):
|
||||||
from .monkeypatching import monkeypatch_all_at_ready
|
from .monkeypatching import monkeypatch_all_at_ready
|
||||||
monkeypatch_all_at_ready()
|
monkeypatch_all_at_ready()
|
||||||
|
|
||||||
|
# Ensure reportlab does not make any calls to the internet or the local disk
|
||||||
|
from reportlab import rl_config
|
||||||
|
rl_config.trustedHosts = []
|
||||||
|
rl_config.trustedSchemes = ['data']
|
||||||
|
|||||||
@@ -46,6 +46,13 @@ class RequestIdFilter(logging.Filter):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class SkipNotFoundFilter(logging.Filter):
|
||||||
|
# Drop the WARNING "Not Found: ..." records django.request emits for 404s
|
||||||
|
# We have different access logs for that
|
||||||
|
def filter(self, record):
|
||||||
|
return getattr(record, 'status_code', None) != 404
|
||||||
|
|
||||||
|
|
||||||
class RequestIdMiddleware:
|
class RequestIdMiddleware:
|
||||||
def __init__(self, get_response):
|
def __init__(self, get_response):
|
||||||
self.get_response = get_response
|
self.get_response = get_response
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ from datetime import datetime
|
|||||||
from http import cookies
|
from http import cookies
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.core.exceptions import SuspiciousFileOperation
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from requests.adapters import HTTPAdapter
|
from requests.adapters import HTTPAdapter
|
||||||
from urllib3.connection import HTTPConnection, HTTPSConnection
|
from urllib3.connection import HTTPConnection, HTTPSConnection
|
||||||
@@ -40,6 +41,8 @@ from urllib3.util.connection import (
|
|||||||
)
|
)
|
||||||
from urllib3.util.timeout import _DEFAULT_TIMEOUT
|
from urllib3.util.timeout import _DEFAULT_TIMEOUT
|
||||||
|
|
||||||
|
from pretix.helpers.reportlab import ThumbnailingImageReader
|
||||||
|
|
||||||
_cgnat_net = ipaddress.ip_network('100.64.0.0/10')
|
_cgnat_net = ipaddress.ip_network('100.64.0.0/10')
|
||||||
|
|
||||||
|
|
||||||
@@ -148,13 +151,14 @@ def monkeypatch_urllib3_ssrf_protection():
|
|||||||
|
|
||||||
if not getattr(settings, "ALLOW_HTTP_TO_PRIVATE_NETWORKS", False):
|
if not getattr(settings, "ALLOW_HTTP_TO_PRIVATE_NETWORKS", False):
|
||||||
ip_addr = ipaddress.ip_address(sa[0])
|
ip_addr = ipaddress.ip_address(sa[0])
|
||||||
|
check_ip4 = ip_addr.ipv4_mapped if getattr(ip_addr, "ipv4_mapped", None) else ip_addr
|
||||||
if ip_addr.is_multicast:
|
if ip_addr.is_multicast:
|
||||||
raise HTTPError(f"Request to multicast address {sa[0]} blocked")
|
raise HTTPError(f"Request to multicast address {sa[0]} blocked")
|
||||||
if ip_addr.is_loopback or ip_addr.is_link_local:
|
if ip_addr.is_loopback or ip_addr.is_link_local:
|
||||||
raise HTTPError(f"Request to local address {sa[0]} blocked")
|
raise HTTPError(f"Request to local address {sa[0]} blocked")
|
||||||
if ip_addr.is_private:
|
if ip_addr.is_private:
|
||||||
raise HTTPError(f"Request to private address {sa[0]} blocked")
|
raise HTTPError(f"Request to private address {sa[0]} blocked")
|
||||||
if ip_addr in _cgnat_net:
|
if check_ip4 in _cgnat_net:
|
||||||
raise HTTPError(f"Request to RFC 6598 address {sa[0]} blocked")
|
raise HTTPError(f"Request to RFC 6598 address {sa[0]} blocked")
|
||||||
|
|
||||||
sock = None
|
sock = None
|
||||||
@@ -230,9 +234,27 @@ def monkeypatch_cookie_morsel():
|
|||||||
cookies.Morsel._reserved.setdefault("partitioned", "Partitioned")
|
cookies.Morsel._reserved.setdefault("partitioned", "Partitioned")
|
||||||
|
|
||||||
|
|
||||||
|
def monkeypatch_reportlab_imagereader():
|
||||||
|
from reportlab.lib import utils
|
||||||
|
old_init = utils.ImageReader.__init__
|
||||||
|
|
||||||
|
def new_init(self, fileName, ident=None): # noqa
|
||||||
|
if not isinstance(fileName, Image.Image) and not hasattr(fileName, 'read') and not hasattr(fileName, 'str'):
|
||||||
|
if not isinstance(self, ThumbnailingImageReader):
|
||||||
|
# ThumbnailingImageReader is only used by us explicitly and not by using <img> in html, so it is safe
|
||||||
|
raise SuspiciousFileOperation("reportlab should not be reading images from disk")
|
||||||
|
|
||||||
|
return types.MethodType(old_init, self)(
|
||||||
|
fileName, ident
|
||||||
|
)
|
||||||
|
|
||||||
|
utils.ImageReader.__init__ = new_init
|
||||||
|
|
||||||
|
|
||||||
def monkeypatch_all_at_ready():
|
def monkeypatch_all_at_ready():
|
||||||
monkeypatch_vobject_performance()
|
monkeypatch_vobject_performance()
|
||||||
monkeypatch_pillow_safer()
|
monkeypatch_pillow_safer()
|
||||||
monkeypatch_requests_timeout()
|
monkeypatch_requests_timeout()
|
||||||
monkeypatch_urllib3_ssrf_protection()
|
monkeypatch_urllib3_ssrf_protection()
|
||||||
monkeypatch_cookie_morsel()
|
monkeypatch_cookie_morsel()
|
||||||
|
monkeypatch_reportlab_imagereader()
|
||||||
|
|||||||
@@ -20,14 +20,19 @@
|
|||||||
# <https://www.gnu.org/licenses/>.
|
# <https://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
import logging
|
import logging
|
||||||
|
import re
|
||||||
|
import unicodedata
|
||||||
|
|
||||||
from arabic_reshaper import ArabicReshaper
|
from arabic_reshaper import ArabicReshaper
|
||||||
|
from bidi import get_display
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.functional import SimpleLazyObject
|
from django.utils.functional import SimpleLazyObject
|
||||||
|
from django.utils.html import escape
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from reportlab.lib.styles import ParagraphStyle
|
from reportlab.lib.styles import ParagraphStyle
|
||||||
from reportlab.lib.utils import ImageReader
|
from reportlab.lib.utils import ImageReader
|
||||||
from reportlab.pdfbase import pdfmetrics
|
from reportlab.pdfbase import pdfmetrics
|
||||||
|
from reportlab.pdfbase.ttfonts import TTFont
|
||||||
from reportlab.platypus import Paragraph
|
from reportlab.platypus import Paragraph
|
||||||
|
|
||||||
from pretix.presale.style import get_fonts
|
from pretix.presale.style import get_fonts
|
||||||
@@ -70,6 +75,20 @@ reshaper = SimpleLazyObject(lambda: ArabicReshaper(configuration={
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_text(text: str) -> str:
|
||||||
|
# reportlab does not support unicode combination characters
|
||||||
|
# It's important we do this before we use ArabicReshaper
|
||||||
|
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.
|
||||||
|
try:
|
||||||
|
text = "\n".join(get_display(reshaper.reshape(l)) for l in re.split("\n", text))
|
||||||
|
except:
|
||||||
|
logger.exception('Reshaping/Bidi fixes failed on string {}'.format(repr(text)))
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
class FontFallbackParagraph(Paragraph):
|
class FontFallbackParagraph(Paragraph):
|
||||||
def __init__(self, text, style=None, *args, **kwargs):
|
def __init__(self, text, style=None, *args, **kwargs):
|
||||||
if style is None:
|
if style is None:
|
||||||
@@ -87,6 +106,8 @@ class FontFallbackParagraph(Paragraph):
|
|||||||
if not text:
|
if not text:
|
||||||
return True
|
return True
|
||||||
font = pdfmetrics.getFont(font_name)
|
font = pdfmetrics.getFont(font_name)
|
||||||
|
if not isinstance(font, TTFont):
|
||||||
|
return True
|
||||||
return all(
|
return all(
|
||||||
ord(c) in font.face.charToGlyph or not c.isprintable()
|
ord(c) in font.face.charToGlyph or not c.isprintable()
|
||||||
for c in text
|
for c in text
|
||||||
@@ -102,6 +123,24 @@ class FontFallbackParagraph(Paragraph):
|
|||||||
return family
|
return family
|
||||||
|
|
||||||
|
|
||||||
|
class PlainTextParagraph(FontFallbackParagraph):
|
||||||
|
def __init__(self, text, style=None, linebreaks=True, *args, **kwargs):
|
||||||
|
if not isinstance(text, str):
|
||||||
|
if hasattr(text, '__html__'):
|
||||||
|
raise ValueError("It is contradictory to pass escaped content to PlainTextParagraph")
|
||||||
|
text = str(text)
|
||||||
|
|
||||||
|
# Normalize unicode and apply reshaping
|
||||||
|
text = normalize_text(text)
|
||||||
|
|
||||||
|
# Escape any HTML in the text
|
||||||
|
text = escape(text)
|
||||||
|
|
||||||
|
if linebreaks:
|
||||||
|
text = text.strip().replace("\n", "<br />\n")
|
||||||
|
super().__init__(text, style, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def register_ttf_font_if_new(name, path):
|
def register_ttf_font_if_new(name, path):
|
||||||
from reportlab.pdfbase import pdfmetrics
|
from reportlab.pdfbase import pdfmetrics
|
||||||
from reportlab.pdfbase.ttfonts import TTFont
|
from reportlab.pdfbase.ttfonts import TTFont
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ from geoip2.errors import AddressNotFoundError
|
|||||||
from pretix.base.i18n import language
|
from pretix.base.i18n import language
|
||||||
from pretix.base.services.mail import mail
|
from pretix.base.services.mail import mail
|
||||||
from pretix.helpers.http import get_client_ip
|
from pretix.helpers.http import get_client_ip
|
||||||
from pretix.helpers.urls import build_absolute_uri
|
from pretix.helpers.urls import mainreverse_absolute
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -175,7 +175,7 @@ def handle_login_source(user, request):
|
|||||||
'source': src,
|
'source': src,
|
||||||
'country': Country(str(country)).name if country else _('Unknown country'),
|
'country': Country(str(country)).name if country else _('Unknown country'),
|
||||||
'instance': settings.PRETIX_INSTANCE_NAME,
|
'instance': settings.PRETIX_INSTANCE_NAME,
|
||||||
'url': build_absolute_uri('control:user.settings')
|
'url': mainreverse_absolute('control:user.settings')
|
||||||
},
|
},
|
||||||
event=None,
|
event=None,
|
||||||
user=user,
|
user=user,
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
#
|
||||||
|
# This file is part of pretix (Community Edition).
|
||||||
|
#
|
||||||
|
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||||
|
# Copyright (C) 2020-today pretix 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 logging
|
||||||
|
|
||||||
|
from django import template
|
||||||
|
from django.utils.html import format_html
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def wrap_in(content, tag_name):
|
||||||
|
return format_html(f'<{tag_name}>{{}}</{tag_name}>', content)
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||||
# <https://www.gnu.org/licenses/>.
|
# <https://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
|
import warnings
|
||||||
from urllib.parse import urljoin
|
from urllib.parse import urljoin
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@@ -26,6 +27,15 @@ from django.urls import reverse
|
|||||||
|
|
||||||
|
|
||||||
def build_absolute_uri(urlname, args=None, kwargs=None):
|
def build_absolute_uri(urlname, args=None, kwargs=None):
|
||||||
|
warnings.warn(
|
||||||
|
'Usage of build_absolute_uri is confusing since there are many functions with that name. '
|
||||||
|
'Replace this usage with ',
|
||||||
|
DeprecationWarning
|
||||||
|
)
|
||||||
|
return mainreverse_absolute(urlname, args, kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def mainreverse_absolute(urlname, args=None, kwargs=None):
|
||||||
from pretix.multidomain import maindomain_urlconf
|
from pretix.multidomain import maindomain_urlconf
|
||||||
|
|
||||||
return urljoin(settings.SITE_URL, reverse(urlname, args=args, kwargs=kwargs, urlconf=maindomain_urlconf))
|
return urljoin(settings.SITE_URL, reverse(urlname, args=args, kwargs=kwargs, urlconf=maindomain_urlconf))
|
||||||
|
|||||||
+2068
-1827
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2026-05-27 14:47+0000\n"
|
"POT-Creation-Date: 2026-06-28 15:49+0000\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@@ -130,6 +130,7 @@ msgid "Mercado Pago"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:167
|
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:167
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:54
|
||||||
#: pretix/static/pretixpresale/js/ui/cart.js:89
|
#: pretix/static/pretixpresale/js/ui/cart.js:89
|
||||||
msgid "Continue"
|
msgid "Continue"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -183,6 +184,172 @@ msgstr ""
|
|||||||
msgid "Contacting your bank …"
|
msgid "Contacting your bank …"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:34
|
||||||
|
msgid "Select a check-in list"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:35
|
||||||
|
msgid "No active check-in lists found."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:36
|
||||||
|
msgid "Switch check-in list"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:37
|
||||||
|
msgid "Search results"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:38
|
||||||
|
msgid "No tickets found"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:39
|
||||||
|
msgid "Result"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:40
|
||||||
|
msgid "This ticket requires special attention"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:41
|
||||||
|
msgid "Switch direction"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:42
|
||||||
|
msgid "Entry"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:43
|
||||||
|
msgid "Exit"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:44
|
||||||
|
msgid "Scan a ticket or search and press return…"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:45
|
||||||
|
msgid "Load more"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:46
|
||||||
|
msgid "Valid"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:47
|
||||||
|
msgid "Unpaid"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:48
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:49
|
||||||
|
msgid "Canceled"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:50
|
||||||
|
msgid "Confirmed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:51
|
||||||
|
msgid "Approval pending"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:52
|
||||||
|
msgid "Redeemed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:53
|
||||||
|
msgid "Cancel"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:55
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:64
|
||||||
|
msgid "Ticket not paid"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:56
|
||||||
|
msgid "This ticket is not yet paid. Do you want to continue anyways?"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:57
|
||||||
|
msgid "Additional information required"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:58
|
||||||
|
msgid "Valid ticket"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:59
|
||||||
|
msgid "Exit recorded"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:60
|
||||||
|
msgid "Ticket already used"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:61
|
||||||
|
msgid "Information required"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:62
|
||||||
|
msgid "Unknown ticket"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:63
|
||||||
|
msgid "Ticket type not allowed here"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:65
|
||||||
|
msgid "Entry not allowed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:66
|
||||||
|
msgid "Ticket code revoked/changed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:67
|
||||||
|
msgid "Ticket blocked"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:68
|
||||||
|
msgid "Ticket not valid at this time"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:69
|
||||||
|
msgid "Order canceled"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:70
|
||||||
|
msgid "Ticket code is ambiguous on list"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:71
|
||||||
|
msgid "Order not approved"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:72
|
||||||
|
msgid "Checked-in Tickets"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:73
|
||||||
|
msgid "Valid Tickets"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:74
|
||||||
|
msgid "Currently inside"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:75
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/question.js:136
|
||||||
|
#: pretix/static/pretixpresale/js/ui/questions.js:271
|
||||||
|
msgid "Yes"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:76
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/question.js:137
|
||||||
|
#: pretix/static/pretixpresale/js/ui/questions.js:271
|
||||||
|
msgid "No"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/lightbox/js/lightbox.js:96
|
#: pretix/static/lightbox/js/lightbox.js:96
|
||||||
msgid "close"
|
msgid "close"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -210,8 +377,8 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixbase/js/asynctask.js:135
|
#: pretix/static/pretixbase/js/asynctask.js:135
|
||||||
#: pretix/static/pretixbase/js/asynctask.js:192
|
#: pretix/static/pretixbase/js/asynctask.js:193
|
||||||
#: pretix/static/pretixbase/js/asynctask.js:196
|
#: pretix/static/pretixbase/js/asynctask.js:197
|
||||||
#: pretix/static/pretixcontrol/js/ui/mail.js:24
|
#: pretix/static/pretixcontrol/js/ui/mail.js:24
|
||||||
msgid "An error of type {code} occurred."
|
msgid "An error of type {code} occurred."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -227,28 +394,28 @@ msgstr ""
|
|||||||
msgid "The request took too long. Please try again."
|
msgid "The request took too long. Please try again."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixbase/js/asynctask.js:198
|
#: pretix/static/pretixbase/js/asynctask.js:199
|
||||||
#: pretix/static/pretixcontrol/js/ui/mail.js:26
|
#: pretix/static/pretixcontrol/js/ui/mail.js:26
|
||||||
msgid ""
|
msgid ""
|
||||||
"We currently cannot reach the server. Please try again. Error code: {code}"
|
"We currently cannot reach the server. Please try again. Error code: {code}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixbase/js/asynctask.js:226
|
#: pretix/static/pretixbase/js/asynctask.js:227
|
||||||
msgid "We are processing your request …"
|
msgid "We are processing your request …"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixbase/js/asynctask.js:229
|
#: pretix/static/pretixbase/js/asynctask.js:230
|
||||||
msgid ""
|
msgid ""
|
||||||
"We are currently sending your request to the server. If this takes longer "
|
"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 "
|
"than one minute, please check your internet connection and then reload this "
|
||||||
"page and try again."
|
"page and try again."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixbase/js/asynctask.js:286
|
#: pretix/static/pretixbase/js/asynctask.js:287
|
||||||
msgid "If this takes longer than a few minutes, please contact us."
|
msgid "If this takes longer than a few minutes, please contact us."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixbase/js/asynctask.js:341
|
#: pretix/static/pretixbase/js/asynctask.js:342
|
||||||
msgid "Close message"
|
msgid "Close message"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -260,6 +427,165 @@ msgstr ""
|
|||||||
msgid "Press Ctrl-C to copy!"
|
msgid "Press Ctrl-C to copy!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/App.vue:80
|
||||||
|
msgid "Edit"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/App.vue:86
|
||||||
|
msgid "Visualize"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/App.vue:96
|
||||||
|
msgid ""
|
||||||
|
"Your rule always filters by product or variation, but the following products "
|
||||||
|
"or variations are not contained in any of your rule parts so people with "
|
||||||
|
"these tickets will not get in:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/App.vue:99
|
||||||
|
msgid "Please double-check if this was intentional."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:4
|
||||||
|
msgid "All of the conditions below (AND)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:5
|
||||||
|
msgid "At least one of the conditions below (OR)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:6
|
||||||
|
msgid "Event start"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:7
|
||||||
|
msgid "Event end"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:8
|
||||||
|
msgid "Event admission"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:9
|
||||||
|
msgid "custom date and time"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:10
|
||||||
|
msgid "custom time"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:11
|
||||||
|
msgid "Tolerance (minutes)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:12
|
||||||
|
msgid "Add condition"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:13
|
||||||
|
msgid "minutes"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:14
|
||||||
|
msgid "Duplicate"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:15
|
||||||
|
msgctxt "entry_status"
|
||||||
|
msgid "present"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:16
|
||||||
|
msgctxt "entry_status"
|
||||||
|
msgid "absent"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:29
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:35
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:41
|
||||||
|
msgid "is one of"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:47
|
||||||
|
msgid "is before"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:51
|
||||||
|
msgid "is after"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:57
|
||||||
|
msgid "="
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:117
|
||||||
|
msgid "Product"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:121
|
||||||
|
msgid "Product variation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:125
|
||||||
|
msgid "Gate"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:129
|
||||||
|
msgid "Current date and time"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:133
|
||||||
|
msgid "Current day of the week (1 = Monday, 7 = Sunday)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:137
|
||||||
|
msgid "Current entry status"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:141
|
||||||
|
msgid "Number of previous entries"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:145
|
||||||
|
msgid "Number of previous entries since midnight"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:149
|
||||||
|
msgid "Number of previous entries since"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:153
|
||||||
|
msgid "Number of previous entries before"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:157
|
||||||
|
msgid "Number of days with a previous entry"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:161
|
||||||
|
msgid "Number of days with a previous entry since"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:165
|
||||||
|
msgid "Number of days with a previous entry before"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:169
|
||||||
|
msgid "Minutes since last entry (-1 on first entry)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:173
|
||||||
|
msgid "Minutes since first entry (-1 on first entry)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/django-interop.ts:47
|
||||||
|
msgid "Error: Product not found!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/django-interop.ts:54
|
||||||
|
msgid "Error: Variation not found!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/editor.js:171
|
#: pretix/static/pretixcontrol/js/ui/editor.js:171
|
||||||
msgid "Check-in QR"
|
msgid "Check-in QR"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -358,23 +684,23 @@ msgstr ""
|
|||||||
msgid "Selected only"
|
msgid "Selected only"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:842
|
#: pretix/static/pretixcontrol/js/ui/main.js:844
|
||||||
msgid "Enter page number between 1 and %(max)s."
|
msgid "Enter page number between 1 and %(max)s."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:845
|
#: pretix/static/pretixcontrol/js/ui/main.js:847
|
||||||
msgid "Invalid page number."
|
msgid "Invalid page number."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:1003
|
#: pretix/static/pretixcontrol/js/ui/main.js:1005
|
||||||
msgid "Use a different name internally"
|
msgid "Use a different name internally"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:1043
|
#: pretix/static/pretixcontrol/js/ui/main.js:1045
|
||||||
msgid "Click to close"
|
msgid "Click to close"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:1124
|
#: pretix/static/pretixcontrol/js/ui/main.js:1126
|
||||||
msgid "You have unsaved changes!"
|
msgid "You have unsaved changes!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -394,16 +720,6 @@ msgstr ""
|
|||||||
msgid "Count"
|
msgid "Count"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/question.js:136
|
|
||||||
#: pretix/static/pretixpresale/js/ui/questions.js:271
|
|
||||||
msgid "Yes"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/question.js:137
|
|
||||||
#: pretix/static/pretixpresale/js/ui/questions.js:271
|
|
||||||
msgid "No"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/subevent.js:112
|
#: pretix/static/pretixcontrol/js/ui/subevent.js:112
|
||||||
msgid "(one more date)"
|
msgid "(one more date)"
|
||||||
msgid_plural "({num} more dates)"
|
msgid_plural "({num} more dates)"
|
||||||
@@ -470,164 +786,196 @@ msgid "Google Pay"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:16
|
#: pretix/static/pretixpresale/js/widget/widget.js:16
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:30
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Quantity"
|
msgid "Quantity"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:31
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Decrease quantity"
|
msgid "Decrease quantity"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:18
|
#: pretix/static/pretixpresale/js/widget/widget.js:18
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:32
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Increase quantity"
|
msgid "Increase quantity"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:19
|
#: pretix/static/pretixpresale/js/widget/widget.js:19
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:33
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Filter events by"
|
msgid "Filter events by"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:20
|
#: pretix/static/pretixpresale/js/widget/widget.js:20
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:34
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Filter"
|
msgid "Filter"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:21
|
#: pretix/static/pretixpresale/js/widget/widget.js:21
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:35
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Price"
|
msgid "Price"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:22
|
#: pretix/static/pretixpresale/js/widget/widget.js:22
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:36
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Original price: %s"
|
msgid "Original price: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:23
|
#: pretix/static/pretixpresale/js/widget/widget.js:23
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:37
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "New price: %s"
|
msgid "New price: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:24
|
#: pretix/static/pretixpresale/js/widget/widget.js:24
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:38
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Select"
|
msgid "Select"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:25
|
#: pretix/static/pretixpresale/js/widget/widget.js:25
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:39
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Select %s"
|
msgid "Select %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:26
|
#: pretix/static/pretixpresale/js/widget/widget.js:26
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:40
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Select variant %s"
|
msgid "Select variant %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:27
|
#: pretix/static/pretixpresale/js/widget/widget.js:27
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:41
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Sold out"
|
msgid "Sold out"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:28
|
#: pretix/static/pretixpresale/js/widget/widget.js:28
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:42
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Buy"
|
msgid "Buy"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:29
|
#: pretix/static/pretixpresale/js/widget/widget.js:29
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:43
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Register"
|
msgid "Register"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:30
|
#: pretix/static/pretixpresale/js/widget/widget.js:30
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:44
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Reserved"
|
msgid "Reserved"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:31
|
#: pretix/static/pretixpresale/js/widget/widget.js:31
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:45
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "FREE"
|
msgid "FREE"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:32
|
#: pretix/static/pretixpresale/js/widget/widget.js:32
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:46
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "from %(currency)s %(price)s"
|
msgid "from %(currency)s %(price)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:33
|
#: pretix/static/pretixpresale/js/widget/widget.js:33
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:47
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Image of %s"
|
msgid "Image of %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:34
|
#: pretix/static/pretixpresale/js/widget/widget.js:34
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:48
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "incl. %(rate)s% %(taxname)s"
|
msgid "incl. %(rate)s% %(taxname)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:35
|
#: pretix/static/pretixpresale/js/widget/widget.js:35
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:49
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "plus %(rate)s% %(taxname)s"
|
msgid "plus %(rate)s% %(taxname)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:36
|
#: pretix/static/pretixpresale/js/widget/widget.js:36
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:50
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "incl. taxes"
|
msgid "incl. taxes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:37
|
#: pretix/static/pretixpresale/js/widget/widget.js:37
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:51
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "plus taxes"
|
msgid "plus taxes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:38
|
#: pretix/static/pretixpresale/js/widget/widget.js:38
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:52
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "currently available: %s"
|
msgid "currently available: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:39
|
#: pretix/static/pretixpresale/js/widget/widget.js:39
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:53
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Only available with a voucher"
|
msgid "Only available with a voucher"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:40
|
#: pretix/static/pretixpresale/js/widget/widget.js:40
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:43
|
#: pretix/static/pretixpresale/js/widget/widget.js:43
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:54
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:57
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Not yet available"
|
msgid "Not yet available"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:41
|
#: pretix/static/pretixpresale/js/widget/widget.js:41
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:55
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Not available anymore"
|
msgid "Not available anymore"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:42
|
#: pretix/static/pretixpresale/js/widget/widget.js:42
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:56
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Currently not available"
|
msgid "Currently not available"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:44
|
#: pretix/static/pretixpresale/js/widget/widget.js:44
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:58
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "minimum amount to order: %s"
|
msgid "minimum amount to order: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:45
|
#: pretix/static/pretixpresale/js/widget/widget.js:45
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:59
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Close ticket shop"
|
msgid "Close ticket shop"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:46
|
#: pretix/static/pretixpresale/js/widget/widget.js:46
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:60
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "The ticket shop could not be loaded."
|
msgid "The ticket shop could not be loaded."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:47
|
#: pretix/static/pretixpresale/js/widget/widget.js:47
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:61
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid ""
|
msgid ""
|
||||||
"There are currently a lot of users in this ticket shop. Please open the shop "
|
"There are currently a lot of users in this ticket shop. Please open the shop "
|
||||||
@@ -635,21 +983,25 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:49
|
#: pretix/static/pretixpresale/js/widget/widget.js:49
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:62
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Open ticket shop"
|
msgid "Open ticket shop"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:50
|
#: pretix/static/pretixpresale/js/widget/widget.js:50
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:63
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Checkout"
|
msgid "Checkout"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:51
|
#: pretix/static/pretixpresale/js/widget/widget.js:51
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:64
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "The cart could not be created. Please try again later"
|
msgid "The cart could not be created. Please try again later"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:52
|
#: pretix/static/pretixpresale/js/widget/widget.js:52
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:65
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid ""
|
msgid ""
|
||||||
"We could not create your cart, since there are currently too many users in "
|
"We could not create your cart, since there are currently too many users in "
|
||||||
@@ -657,11 +1009,13 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:54
|
#: pretix/static/pretixpresale/js/widget/widget.js:54
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:66
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Waiting list"
|
msgid "Waiting list"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:55
|
#: pretix/static/pretixpresale/js/widget/widget.js:55
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:67
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid ""
|
msgid ""
|
||||||
"You currently have an active cart for this event. If you select more "
|
"You currently have an active cart for this event. If you select more "
|
||||||
@@ -669,96 +1023,115 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:57
|
#: pretix/static/pretixpresale/js/widget/widget.js:57
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:68
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Resume checkout"
|
msgid "Resume checkout"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:58
|
#: pretix/static/pretixpresale/js/widget/widget.js:58
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:69
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Redeem a voucher"
|
msgid "Redeem a voucher"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:59
|
#: pretix/static/pretixpresale/js/widget/widget.js:59
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:70
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Redeem"
|
msgid "Redeem"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:60
|
#: pretix/static/pretixpresale/js/widget/widget.js:60
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:71
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Voucher code"
|
msgid "Voucher code"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:61
|
#: pretix/static/pretixpresale/js/widget/widget.js:61
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:72
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Close"
|
msgid "Close"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:62
|
#: pretix/static/pretixpresale/js/widget/widget.js:62
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:73
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Close checkout"
|
msgid "Close checkout"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:63
|
#: pretix/static/pretixpresale/js/widget/widget.js:63
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:74
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "You cannot cancel this operation. Please wait for loading to finish."
|
msgid "You cannot cancel this operation. Please wait for loading to finish."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:64
|
#: pretix/static/pretixpresale/js/widget/widget.js:64
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:75
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Continue"
|
msgid "Continue"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:65
|
#: pretix/static/pretixpresale/js/widget/widget.js:65
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:76
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Show variants"
|
msgid "Show variants"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:66
|
#: pretix/static/pretixpresale/js/widget/widget.js:66
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:77
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Hide variants"
|
msgid "Hide variants"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:67
|
#: pretix/static/pretixpresale/js/widget/widget.js:67
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:78
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Choose a different event"
|
msgid "Choose a different event"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:68
|
#: pretix/static/pretixpresale/js/widget/widget.js:68
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:79
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Choose a different date"
|
msgid "Choose a different date"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:69
|
#: pretix/static/pretixpresale/js/widget/widget.js:69
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:80
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Back"
|
msgid "Back"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:70
|
#: pretix/static/pretixpresale/js/widget/widget.js:70
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:81
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Next month"
|
msgid "Next month"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:71
|
#: pretix/static/pretixpresale/js/widget/widget.js:71
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:82
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Previous month"
|
msgid "Previous month"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:72
|
#: pretix/static/pretixpresale/js/widget/widget.js:72
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:83
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Next week"
|
msgid "Next week"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:73
|
#: pretix/static/pretixpresale/js/widget/widget.js:73
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:84
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Previous week"
|
msgid "Previous week"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:74
|
#: pretix/static/pretixpresale/js/widget/widget.js:74
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:85
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Open seat selection"
|
msgid "Open seat selection"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:75
|
#: pretix/static/pretixpresale/js/widget/widget.js:75
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:86
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid ""
|
msgid ""
|
||||||
"Some or all ticket categories are currently sold out. If you want, you can "
|
"Some or all ticket categories are currently sold out. If you want, you can "
|
||||||
@@ -767,110 +1140,137 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:76
|
#: pretix/static/pretixpresale/js/widget/widget.js:76
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:87
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Load more"
|
msgid "Load more"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:78
|
#: pretix/static/pretixpresale/js/widget/widget.js:78
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:89
|
||||||
msgid "Mo"
|
msgid "Mo"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:79
|
#: pretix/static/pretixpresale/js/widget/widget.js:79
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:90
|
||||||
msgid "Tu"
|
msgid "Tu"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:80
|
#: pretix/static/pretixpresale/js/widget/widget.js:80
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:91
|
||||||
msgid "We"
|
msgid "We"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:81
|
#: pretix/static/pretixpresale/js/widget/widget.js:81
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:92
|
||||||
msgid "Th"
|
msgid "Th"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:82
|
#: pretix/static/pretixpresale/js/widget/widget.js:82
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:93
|
||||||
msgid "Fr"
|
msgid "Fr"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:83
|
#: pretix/static/pretixpresale/js/widget/widget.js:83
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:94
|
||||||
msgid "Sa"
|
msgid "Sa"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:84
|
#: pretix/static/pretixpresale/js/widget/widget.js:84
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:95
|
||||||
msgid "Su"
|
msgid "Su"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:85
|
#: pretix/static/pretixpresale/js/widget/widget.js:85
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:96
|
||||||
msgid "Monday"
|
msgid "Monday"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:86
|
#: pretix/static/pretixpresale/js/widget/widget.js:86
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:97
|
||||||
msgid "Tuesday"
|
msgid "Tuesday"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:87
|
#: pretix/static/pretixpresale/js/widget/widget.js:87
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:98
|
||||||
msgid "Wednesday"
|
msgid "Wednesday"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:88
|
#: pretix/static/pretixpresale/js/widget/widget.js:88
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:99
|
||||||
msgid "Thursday"
|
msgid "Thursday"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:89
|
#: pretix/static/pretixpresale/js/widget/widget.js:89
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:100
|
||||||
msgid "Friday"
|
msgid "Friday"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:90
|
#: pretix/static/pretixpresale/js/widget/widget.js:90
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:101
|
||||||
msgid "Saturday"
|
msgid "Saturday"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:91
|
#: pretix/static/pretixpresale/js/widget/widget.js:91
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:102
|
||||||
msgid "Sunday"
|
msgid "Sunday"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:94
|
#: pretix/static/pretixpresale/js/widget/widget.js:94
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:105
|
||||||
msgid "January"
|
msgid "January"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:95
|
#: pretix/static/pretixpresale/js/widget/widget.js:95
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:106
|
||||||
msgid "February"
|
msgid "February"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:96
|
#: pretix/static/pretixpresale/js/widget/widget.js:96
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:107
|
||||||
msgid "March"
|
msgid "March"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:97
|
#: pretix/static/pretixpresale/js/widget/widget.js:97
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:108
|
||||||
msgid "April"
|
msgid "April"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:98
|
#: pretix/static/pretixpresale/js/widget/widget.js:98
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:109
|
||||||
msgid "May"
|
msgid "May"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:99
|
#: pretix/static/pretixpresale/js/widget/widget.js:99
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:110
|
||||||
msgid "June"
|
msgid "June"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:100
|
#: pretix/static/pretixpresale/js/widget/widget.js:100
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:111
|
||||||
msgid "July"
|
msgid "July"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:101
|
#: pretix/static/pretixpresale/js/widget/widget.js:101
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:112
|
||||||
msgid "August"
|
msgid "August"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:102
|
#: pretix/static/pretixpresale/js/widget/widget.js:102
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:113
|
||||||
msgid "September"
|
msgid "September"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:103
|
#: pretix/static/pretixpresale/js/widget/widget.js:103
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:114
|
||||||
msgid "October"
|
msgid "October"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:104
|
#: pretix/static/pretixpresale/js/widget/widget.js:104
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:115
|
||||||
msgid "November"
|
msgid "November"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:105
|
#: pretix/static/pretixpresale/js/widget/widget.js:105
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:116
|
||||||
msgid "December"
|
msgid "December"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
+2086
-1803
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+2068
-1827
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2026-05-27 14:47+0000\n"
|
"POT-Creation-Date: 2026-06-28 15:49+0000\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@@ -130,6 +130,7 @@ msgid "Mercado Pago"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:167
|
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:167
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:54
|
||||||
#: pretix/static/pretixpresale/js/ui/cart.js:89
|
#: pretix/static/pretixpresale/js/ui/cart.js:89
|
||||||
msgid "Continue"
|
msgid "Continue"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -183,6 +184,172 @@ msgstr ""
|
|||||||
msgid "Contacting your bank …"
|
msgid "Contacting your bank …"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:34
|
||||||
|
msgid "Select a check-in list"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:35
|
||||||
|
msgid "No active check-in lists found."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:36
|
||||||
|
msgid "Switch check-in list"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:37
|
||||||
|
msgid "Search results"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:38
|
||||||
|
msgid "No tickets found"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:39
|
||||||
|
msgid "Result"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:40
|
||||||
|
msgid "This ticket requires special attention"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:41
|
||||||
|
msgid "Switch direction"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:42
|
||||||
|
msgid "Entry"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:43
|
||||||
|
msgid "Exit"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:44
|
||||||
|
msgid "Scan a ticket or search and press return…"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:45
|
||||||
|
msgid "Load more"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:46
|
||||||
|
msgid "Valid"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:47
|
||||||
|
msgid "Unpaid"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:48
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:49
|
||||||
|
msgid "Canceled"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:50
|
||||||
|
msgid "Confirmed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:51
|
||||||
|
msgid "Approval pending"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:52
|
||||||
|
msgid "Redeemed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:53
|
||||||
|
msgid "Cancel"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:55
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:64
|
||||||
|
msgid "Ticket not paid"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:56
|
||||||
|
msgid "This ticket is not yet paid. Do you want to continue anyways?"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:57
|
||||||
|
msgid "Additional information required"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:58
|
||||||
|
msgid "Valid ticket"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:59
|
||||||
|
msgid "Exit recorded"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:60
|
||||||
|
msgid "Ticket already used"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:61
|
||||||
|
msgid "Information required"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:62
|
||||||
|
msgid "Unknown ticket"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:63
|
||||||
|
msgid "Ticket type not allowed here"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:65
|
||||||
|
msgid "Entry not allowed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:66
|
||||||
|
msgid "Ticket code revoked/changed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:67
|
||||||
|
msgid "Ticket blocked"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:68
|
||||||
|
msgid "Ticket not valid at this time"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:69
|
||||||
|
msgid "Order canceled"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:70
|
||||||
|
msgid "Ticket code is ambiguous on list"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:71
|
||||||
|
msgid "Order not approved"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:72
|
||||||
|
msgid "Checked-in Tickets"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:73
|
||||||
|
msgid "Valid Tickets"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:74
|
||||||
|
msgid "Currently inside"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:75
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/question.js:136
|
||||||
|
#: pretix/static/pretixpresale/js/ui/questions.js:271
|
||||||
|
msgid "Yes"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:76
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/question.js:137
|
||||||
|
#: pretix/static/pretixpresale/js/ui/questions.js:271
|
||||||
|
msgid "No"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/lightbox/js/lightbox.js:96
|
#: pretix/static/lightbox/js/lightbox.js:96
|
||||||
msgid "close"
|
msgid "close"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -210,8 +377,8 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixbase/js/asynctask.js:135
|
#: pretix/static/pretixbase/js/asynctask.js:135
|
||||||
#: pretix/static/pretixbase/js/asynctask.js:192
|
#: pretix/static/pretixbase/js/asynctask.js:193
|
||||||
#: pretix/static/pretixbase/js/asynctask.js:196
|
#: pretix/static/pretixbase/js/asynctask.js:197
|
||||||
#: pretix/static/pretixcontrol/js/ui/mail.js:24
|
#: pretix/static/pretixcontrol/js/ui/mail.js:24
|
||||||
msgid "An error of type {code} occurred."
|
msgid "An error of type {code} occurred."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -227,28 +394,28 @@ msgstr ""
|
|||||||
msgid "The request took too long. Please try again."
|
msgid "The request took too long. Please try again."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixbase/js/asynctask.js:198
|
#: pretix/static/pretixbase/js/asynctask.js:199
|
||||||
#: pretix/static/pretixcontrol/js/ui/mail.js:26
|
#: pretix/static/pretixcontrol/js/ui/mail.js:26
|
||||||
msgid ""
|
msgid ""
|
||||||
"We currently cannot reach the server. Please try again. Error code: {code}"
|
"We currently cannot reach the server. Please try again. Error code: {code}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixbase/js/asynctask.js:226
|
#: pretix/static/pretixbase/js/asynctask.js:227
|
||||||
msgid "We are processing your request …"
|
msgid "We are processing your request …"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixbase/js/asynctask.js:229
|
#: pretix/static/pretixbase/js/asynctask.js:230
|
||||||
msgid ""
|
msgid ""
|
||||||
"We are currently sending your request to the server. If this takes longer "
|
"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 "
|
"than one minute, please check your internet connection and then reload this "
|
||||||
"page and try again."
|
"page and try again."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixbase/js/asynctask.js:286
|
#: pretix/static/pretixbase/js/asynctask.js:287
|
||||||
msgid "If this takes longer than a few minutes, please contact us."
|
msgid "If this takes longer than a few minutes, please contact us."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixbase/js/asynctask.js:341
|
#: pretix/static/pretixbase/js/asynctask.js:342
|
||||||
msgid "Close message"
|
msgid "Close message"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -260,6 +427,165 @@ msgstr ""
|
|||||||
msgid "Press Ctrl-C to copy!"
|
msgid "Press Ctrl-C to copy!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/App.vue:80
|
||||||
|
msgid "Edit"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/App.vue:86
|
||||||
|
msgid "Visualize"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/App.vue:96
|
||||||
|
msgid ""
|
||||||
|
"Your rule always filters by product or variation, but the following products "
|
||||||
|
"or variations are not contained in any of your rule parts so people with "
|
||||||
|
"these tickets will not get in:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/App.vue:99
|
||||||
|
msgid "Please double-check if this was intentional."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:4
|
||||||
|
msgid "All of the conditions below (AND)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:5
|
||||||
|
msgid "At least one of the conditions below (OR)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:6
|
||||||
|
msgid "Event start"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:7
|
||||||
|
msgid "Event end"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:8
|
||||||
|
msgid "Event admission"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:9
|
||||||
|
msgid "custom date and time"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:10
|
||||||
|
msgid "custom time"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:11
|
||||||
|
msgid "Tolerance (minutes)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:12
|
||||||
|
msgid "Add condition"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:13
|
||||||
|
msgid "minutes"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:14
|
||||||
|
msgid "Duplicate"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:15
|
||||||
|
msgctxt "entry_status"
|
||||||
|
msgid "present"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:16
|
||||||
|
msgctxt "entry_status"
|
||||||
|
msgid "absent"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:29
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:35
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:41
|
||||||
|
msgid "is one of"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:47
|
||||||
|
msgid "is before"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:51
|
||||||
|
msgid "is after"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:57
|
||||||
|
msgid "="
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:117
|
||||||
|
msgid "Product"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:121
|
||||||
|
msgid "Product variation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:125
|
||||||
|
msgid "Gate"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:129
|
||||||
|
msgid "Current date and time"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:133
|
||||||
|
msgid "Current day of the week (1 = Monday, 7 = Sunday)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:137
|
||||||
|
msgid "Current entry status"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:141
|
||||||
|
msgid "Number of previous entries"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:145
|
||||||
|
msgid "Number of previous entries since midnight"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:149
|
||||||
|
msgid "Number of previous entries since"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:153
|
||||||
|
msgid "Number of previous entries before"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:157
|
||||||
|
msgid "Number of days with a previous entry"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:161
|
||||||
|
msgid "Number of days with a previous entry since"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:165
|
||||||
|
msgid "Number of days with a previous entry before"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:169
|
||||||
|
msgid "Minutes since last entry (-1 on first entry)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:173
|
||||||
|
msgid "Minutes since first entry (-1 on first entry)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/django-interop.ts:47
|
||||||
|
msgid "Error: Product not found!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/django-interop.ts:54
|
||||||
|
msgid "Error: Variation not found!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/editor.js:171
|
#: pretix/static/pretixcontrol/js/ui/editor.js:171
|
||||||
msgid "Check-in QR"
|
msgid "Check-in QR"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -358,23 +684,23 @@ msgstr ""
|
|||||||
msgid "Selected only"
|
msgid "Selected only"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:842
|
#: pretix/static/pretixcontrol/js/ui/main.js:844
|
||||||
msgid "Enter page number between 1 and %(max)s."
|
msgid "Enter page number between 1 and %(max)s."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:845
|
#: pretix/static/pretixcontrol/js/ui/main.js:847
|
||||||
msgid "Invalid page number."
|
msgid "Invalid page number."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:1003
|
#: pretix/static/pretixcontrol/js/ui/main.js:1005
|
||||||
msgid "Use a different name internally"
|
msgid "Use a different name internally"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:1043
|
#: pretix/static/pretixcontrol/js/ui/main.js:1045
|
||||||
msgid "Click to close"
|
msgid "Click to close"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:1124
|
#: pretix/static/pretixcontrol/js/ui/main.js:1126
|
||||||
msgid "You have unsaved changes!"
|
msgid "You have unsaved changes!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -394,16 +720,6 @@ msgstr ""
|
|||||||
msgid "Count"
|
msgid "Count"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/question.js:136
|
|
||||||
#: pretix/static/pretixpresale/js/ui/questions.js:271
|
|
||||||
msgid "Yes"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/question.js:137
|
|
||||||
#: pretix/static/pretixpresale/js/ui/questions.js:271
|
|
||||||
msgid "No"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/subevent.js:112
|
#: pretix/static/pretixcontrol/js/ui/subevent.js:112
|
||||||
msgid "(one more date)"
|
msgid "(one more date)"
|
||||||
msgid_plural "({num} more dates)"
|
msgid_plural "({num} more dates)"
|
||||||
@@ -470,164 +786,196 @@ msgid "Google Pay"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:16
|
#: pretix/static/pretixpresale/js/widget/widget.js:16
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:30
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Quantity"
|
msgid "Quantity"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:31
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Decrease quantity"
|
msgid "Decrease quantity"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:18
|
#: pretix/static/pretixpresale/js/widget/widget.js:18
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:32
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Increase quantity"
|
msgid "Increase quantity"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:19
|
#: pretix/static/pretixpresale/js/widget/widget.js:19
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:33
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Filter events by"
|
msgid "Filter events by"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:20
|
#: pretix/static/pretixpresale/js/widget/widget.js:20
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:34
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Filter"
|
msgid "Filter"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:21
|
#: pretix/static/pretixpresale/js/widget/widget.js:21
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:35
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Price"
|
msgid "Price"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:22
|
#: pretix/static/pretixpresale/js/widget/widget.js:22
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:36
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Original price: %s"
|
msgid "Original price: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:23
|
#: pretix/static/pretixpresale/js/widget/widget.js:23
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:37
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "New price: %s"
|
msgid "New price: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:24
|
#: pretix/static/pretixpresale/js/widget/widget.js:24
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:38
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Select"
|
msgid "Select"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:25
|
#: pretix/static/pretixpresale/js/widget/widget.js:25
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:39
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Select %s"
|
msgid "Select %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:26
|
#: pretix/static/pretixpresale/js/widget/widget.js:26
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:40
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Select variant %s"
|
msgid "Select variant %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:27
|
#: pretix/static/pretixpresale/js/widget/widget.js:27
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:41
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Sold out"
|
msgid "Sold out"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:28
|
#: pretix/static/pretixpresale/js/widget/widget.js:28
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:42
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Buy"
|
msgid "Buy"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:29
|
#: pretix/static/pretixpresale/js/widget/widget.js:29
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:43
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Register"
|
msgid "Register"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:30
|
#: pretix/static/pretixpresale/js/widget/widget.js:30
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:44
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Reserved"
|
msgid "Reserved"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:31
|
#: pretix/static/pretixpresale/js/widget/widget.js:31
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:45
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "FREE"
|
msgid "FREE"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:32
|
#: pretix/static/pretixpresale/js/widget/widget.js:32
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:46
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "from %(currency)s %(price)s"
|
msgid "from %(currency)s %(price)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:33
|
#: pretix/static/pretixpresale/js/widget/widget.js:33
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:47
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Image of %s"
|
msgid "Image of %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:34
|
#: pretix/static/pretixpresale/js/widget/widget.js:34
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:48
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "incl. %(rate)s% %(taxname)s"
|
msgid "incl. %(rate)s% %(taxname)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:35
|
#: pretix/static/pretixpresale/js/widget/widget.js:35
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:49
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "plus %(rate)s% %(taxname)s"
|
msgid "plus %(rate)s% %(taxname)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:36
|
#: pretix/static/pretixpresale/js/widget/widget.js:36
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:50
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "incl. taxes"
|
msgid "incl. taxes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:37
|
#: pretix/static/pretixpresale/js/widget/widget.js:37
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:51
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "plus taxes"
|
msgid "plus taxes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:38
|
#: pretix/static/pretixpresale/js/widget/widget.js:38
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:52
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "currently available: %s"
|
msgid "currently available: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:39
|
#: pretix/static/pretixpresale/js/widget/widget.js:39
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:53
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Only available with a voucher"
|
msgid "Only available with a voucher"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:40
|
#: pretix/static/pretixpresale/js/widget/widget.js:40
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:43
|
#: pretix/static/pretixpresale/js/widget/widget.js:43
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:54
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:57
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Not yet available"
|
msgid "Not yet available"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:41
|
#: pretix/static/pretixpresale/js/widget/widget.js:41
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:55
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Not available anymore"
|
msgid "Not available anymore"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:42
|
#: pretix/static/pretixpresale/js/widget/widget.js:42
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:56
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Currently not available"
|
msgid "Currently not available"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:44
|
#: pretix/static/pretixpresale/js/widget/widget.js:44
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:58
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "minimum amount to order: %s"
|
msgid "minimum amount to order: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:45
|
#: pretix/static/pretixpresale/js/widget/widget.js:45
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:59
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Close ticket shop"
|
msgid "Close ticket shop"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:46
|
#: pretix/static/pretixpresale/js/widget/widget.js:46
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:60
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "The ticket shop could not be loaded."
|
msgid "The ticket shop could not be loaded."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:47
|
#: pretix/static/pretixpresale/js/widget/widget.js:47
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:61
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid ""
|
msgid ""
|
||||||
"There are currently a lot of users in this ticket shop. Please open the shop "
|
"There are currently a lot of users in this ticket shop. Please open the shop "
|
||||||
@@ -635,21 +983,25 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:49
|
#: pretix/static/pretixpresale/js/widget/widget.js:49
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:62
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Open ticket shop"
|
msgid "Open ticket shop"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:50
|
#: pretix/static/pretixpresale/js/widget/widget.js:50
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:63
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Checkout"
|
msgid "Checkout"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:51
|
#: pretix/static/pretixpresale/js/widget/widget.js:51
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:64
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "The cart could not be created. Please try again later"
|
msgid "The cart could not be created. Please try again later"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:52
|
#: pretix/static/pretixpresale/js/widget/widget.js:52
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:65
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid ""
|
msgid ""
|
||||||
"We could not create your cart, since there are currently too many users in "
|
"We could not create your cart, since there are currently too many users in "
|
||||||
@@ -657,11 +1009,13 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:54
|
#: pretix/static/pretixpresale/js/widget/widget.js:54
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:66
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Waiting list"
|
msgid "Waiting list"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:55
|
#: pretix/static/pretixpresale/js/widget/widget.js:55
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:67
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid ""
|
msgid ""
|
||||||
"You currently have an active cart for this event. If you select more "
|
"You currently have an active cart for this event. If you select more "
|
||||||
@@ -669,96 +1023,115 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:57
|
#: pretix/static/pretixpresale/js/widget/widget.js:57
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:68
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Resume checkout"
|
msgid "Resume checkout"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:58
|
#: pretix/static/pretixpresale/js/widget/widget.js:58
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:69
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Redeem a voucher"
|
msgid "Redeem a voucher"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:59
|
#: pretix/static/pretixpresale/js/widget/widget.js:59
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:70
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Redeem"
|
msgid "Redeem"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:60
|
#: pretix/static/pretixpresale/js/widget/widget.js:60
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:71
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Voucher code"
|
msgid "Voucher code"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:61
|
#: pretix/static/pretixpresale/js/widget/widget.js:61
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:72
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Close"
|
msgid "Close"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:62
|
#: pretix/static/pretixpresale/js/widget/widget.js:62
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:73
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Close checkout"
|
msgid "Close checkout"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:63
|
#: pretix/static/pretixpresale/js/widget/widget.js:63
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:74
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "You cannot cancel this operation. Please wait for loading to finish."
|
msgid "You cannot cancel this operation. Please wait for loading to finish."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:64
|
#: pretix/static/pretixpresale/js/widget/widget.js:64
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:75
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Continue"
|
msgid "Continue"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:65
|
#: pretix/static/pretixpresale/js/widget/widget.js:65
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:76
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Show variants"
|
msgid "Show variants"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:66
|
#: pretix/static/pretixpresale/js/widget/widget.js:66
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:77
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Hide variants"
|
msgid "Hide variants"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:67
|
#: pretix/static/pretixpresale/js/widget/widget.js:67
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:78
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Choose a different event"
|
msgid "Choose a different event"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:68
|
#: pretix/static/pretixpresale/js/widget/widget.js:68
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:79
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Choose a different date"
|
msgid "Choose a different date"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:69
|
#: pretix/static/pretixpresale/js/widget/widget.js:69
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:80
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Back"
|
msgid "Back"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:70
|
#: pretix/static/pretixpresale/js/widget/widget.js:70
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:81
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Next month"
|
msgid "Next month"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:71
|
#: pretix/static/pretixpresale/js/widget/widget.js:71
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:82
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Previous month"
|
msgid "Previous month"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:72
|
#: pretix/static/pretixpresale/js/widget/widget.js:72
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:83
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Next week"
|
msgid "Next week"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:73
|
#: pretix/static/pretixpresale/js/widget/widget.js:73
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:84
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Previous week"
|
msgid "Previous week"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:74
|
#: pretix/static/pretixpresale/js/widget/widget.js:74
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:85
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Open seat selection"
|
msgid "Open seat selection"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:75
|
#: pretix/static/pretixpresale/js/widget/widget.js:75
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:86
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid ""
|
msgid ""
|
||||||
"Some or all ticket categories are currently sold out. If you want, you can "
|
"Some or all ticket categories are currently sold out. If you want, you can "
|
||||||
@@ -767,110 +1140,137 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:76
|
#: pretix/static/pretixpresale/js/widget/widget.js:76
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:87
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Load more"
|
msgid "Load more"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:78
|
#: pretix/static/pretixpresale/js/widget/widget.js:78
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:89
|
||||||
msgid "Mo"
|
msgid "Mo"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:79
|
#: pretix/static/pretixpresale/js/widget/widget.js:79
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:90
|
||||||
msgid "Tu"
|
msgid "Tu"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:80
|
#: pretix/static/pretixpresale/js/widget/widget.js:80
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:91
|
||||||
msgid "We"
|
msgid "We"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:81
|
#: pretix/static/pretixpresale/js/widget/widget.js:81
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:92
|
||||||
msgid "Th"
|
msgid "Th"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:82
|
#: pretix/static/pretixpresale/js/widget/widget.js:82
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:93
|
||||||
msgid "Fr"
|
msgid "Fr"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:83
|
#: pretix/static/pretixpresale/js/widget/widget.js:83
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:94
|
||||||
msgid "Sa"
|
msgid "Sa"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:84
|
#: pretix/static/pretixpresale/js/widget/widget.js:84
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:95
|
||||||
msgid "Su"
|
msgid "Su"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:85
|
#: pretix/static/pretixpresale/js/widget/widget.js:85
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:96
|
||||||
msgid "Monday"
|
msgid "Monday"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:86
|
#: pretix/static/pretixpresale/js/widget/widget.js:86
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:97
|
||||||
msgid "Tuesday"
|
msgid "Tuesday"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:87
|
#: pretix/static/pretixpresale/js/widget/widget.js:87
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:98
|
||||||
msgid "Wednesday"
|
msgid "Wednesday"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:88
|
#: pretix/static/pretixpresale/js/widget/widget.js:88
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:99
|
||||||
msgid "Thursday"
|
msgid "Thursday"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:89
|
#: pretix/static/pretixpresale/js/widget/widget.js:89
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:100
|
||||||
msgid "Friday"
|
msgid "Friday"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:90
|
#: pretix/static/pretixpresale/js/widget/widget.js:90
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:101
|
||||||
msgid "Saturday"
|
msgid "Saturday"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:91
|
#: pretix/static/pretixpresale/js/widget/widget.js:91
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:102
|
||||||
msgid "Sunday"
|
msgid "Sunday"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:94
|
#: pretix/static/pretixpresale/js/widget/widget.js:94
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:105
|
||||||
msgid "January"
|
msgid "January"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:95
|
#: pretix/static/pretixpresale/js/widget/widget.js:95
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:106
|
||||||
msgid "February"
|
msgid "February"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:96
|
#: pretix/static/pretixpresale/js/widget/widget.js:96
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:107
|
||||||
msgid "March"
|
msgid "March"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:97
|
#: pretix/static/pretixpresale/js/widget/widget.js:97
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:108
|
||||||
msgid "April"
|
msgid "April"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:98
|
#: pretix/static/pretixpresale/js/widget/widget.js:98
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:109
|
||||||
msgid "May"
|
msgid "May"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:99
|
#: pretix/static/pretixpresale/js/widget/widget.js:99
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:110
|
||||||
msgid "June"
|
msgid "June"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:100
|
#: pretix/static/pretixpresale/js/widget/widget.js:100
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:111
|
||||||
msgid "July"
|
msgid "July"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:101
|
#: pretix/static/pretixpresale/js/widget/widget.js:101
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:112
|
||||||
msgid "August"
|
msgid "August"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:102
|
#: pretix/static/pretixpresale/js/widget/widget.js:102
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:113
|
||||||
msgid "September"
|
msgid "September"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:103
|
#: pretix/static/pretixpresale/js/widget/widget.js:103
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:114
|
||||||
msgid "October"
|
msgid "October"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:104
|
#: pretix/static/pretixpresale/js/widget/widget.js:104
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:115
|
||||||
msgid "November"
|
msgid "November"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:105
|
#: pretix/static/pretixpresale/js/widget/widget.js:105
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:116
|
||||||
msgid "December"
|
msgid "December"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
+2103
-1815
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+2095
-1804
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+2070
-1827
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2026-05-27 14:47+0000\n"
|
"POT-Creation-Date: 2026-06-28 15:49+0000\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@@ -131,6 +131,7 @@ msgid "Mercado Pago"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:167
|
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:167
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:54
|
||||||
#: pretix/static/pretixpresale/js/ui/cart.js:89
|
#: pretix/static/pretixpresale/js/ui/cart.js:89
|
||||||
msgid "Continue"
|
msgid "Continue"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -184,6 +185,172 @@ msgstr ""
|
|||||||
msgid "Contacting your bank …"
|
msgid "Contacting your bank …"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:34
|
||||||
|
msgid "Select a check-in list"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:35
|
||||||
|
msgid "No active check-in lists found."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:36
|
||||||
|
msgid "Switch check-in list"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:37
|
||||||
|
msgid "Search results"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:38
|
||||||
|
msgid "No tickets found"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:39
|
||||||
|
msgid "Result"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:40
|
||||||
|
msgid "This ticket requires special attention"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:41
|
||||||
|
msgid "Switch direction"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:42
|
||||||
|
msgid "Entry"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:43
|
||||||
|
msgid "Exit"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:44
|
||||||
|
msgid "Scan a ticket or search and press return…"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:45
|
||||||
|
msgid "Load more"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:46
|
||||||
|
msgid "Valid"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:47
|
||||||
|
msgid "Unpaid"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:48
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:49
|
||||||
|
msgid "Canceled"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:50
|
||||||
|
msgid "Confirmed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:51
|
||||||
|
msgid "Approval pending"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:52
|
||||||
|
msgid "Redeemed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:53
|
||||||
|
msgid "Cancel"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:55
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:64
|
||||||
|
msgid "Ticket not paid"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:56
|
||||||
|
msgid "This ticket is not yet paid. Do you want to continue anyways?"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:57
|
||||||
|
msgid "Additional information required"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:58
|
||||||
|
msgid "Valid ticket"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:59
|
||||||
|
msgid "Exit recorded"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:60
|
||||||
|
msgid "Ticket already used"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:61
|
||||||
|
msgid "Information required"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:62
|
||||||
|
msgid "Unknown ticket"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:63
|
||||||
|
msgid "Ticket type not allowed here"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:65
|
||||||
|
msgid "Entry not allowed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:66
|
||||||
|
msgid "Ticket code revoked/changed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:67
|
||||||
|
msgid "Ticket blocked"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:68
|
||||||
|
msgid "Ticket not valid at this time"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:69
|
||||||
|
msgid "Order canceled"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:70
|
||||||
|
msgid "Ticket code is ambiguous on list"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:71
|
||||||
|
msgid "Order not approved"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:72
|
||||||
|
msgid "Checked-in Tickets"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:73
|
||||||
|
msgid "Valid Tickets"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:74
|
||||||
|
msgid "Currently inside"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:75
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/question.js:136
|
||||||
|
#: pretix/static/pretixpresale/js/ui/questions.js:271
|
||||||
|
msgid "Yes"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/i18n.ts:76
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/question.js:137
|
||||||
|
#: pretix/static/pretixpresale/js/ui/questions.js:271
|
||||||
|
msgid "No"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/lightbox/js/lightbox.js:96
|
#: pretix/static/lightbox/js/lightbox.js:96
|
||||||
msgid "close"
|
msgid "close"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -211,8 +378,8 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixbase/js/asynctask.js:135
|
#: pretix/static/pretixbase/js/asynctask.js:135
|
||||||
#: pretix/static/pretixbase/js/asynctask.js:192
|
#: pretix/static/pretixbase/js/asynctask.js:193
|
||||||
#: pretix/static/pretixbase/js/asynctask.js:196
|
#: pretix/static/pretixbase/js/asynctask.js:197
|
||||||
#: pretix/static/pretixcontrol/js/ui/mail.js:24
|
#: pretix/static/pretixcontrol/js/ui/mail.js:24
|
||||||
msgid "An error of type {code} occurred."
|
msgid "An error of type {code} occurred."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -228,28 +395,28 @@ msgstr ""
|
|||||||
msgid "The request took too long. Please try again."
|
msgid "The request took too long. Please try again."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixbase/js/asynctask.js:198
|
#: pretix/static/pretixbase/js/asynctask.js:199
|
||||||
#: pretix/static/pretixcontrol/js/ui/mail.js:26
|
#: pretix/static/pretixcontrol/js/ui/mail.js:26
|
||||||
msgid ""
|
msgid ""
|
||||||
"We currently cannot reach the server. Please try again. Error code: {code}"
|
"We currently cannot reach the server. Please try again. Error code: {code}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixbase/js/asynctask.js:226
|
#: pretix/static/pretixbase/js/asynctask.js:227
|
||||||
msgid "We are processing your request …"
|
msgid "We are processing your request …"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixbase/js/asynctask.js:229
|
#: pretix/static/pretixbase/js/asynctask.js:230
|
||||||
msgid ""
|
msgid ""
|
||||||
"We are currently sending your request to the server. If this takes longer "
|
"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 "
|
"than one minute, please check your internet connection and then reload this "
|
||||||
"page and try again."
|
"page and try again."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixbase/js/asynctask.js:286
|
#: pretix/static/pretixbase/js/asynctask.js:287
|
||||||
msgid "If this takes longer than a few minutes, please contact us."
|
msgid "If this takes longer than a few minutes, please contact us."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixbase/js/asynctask.js:341
|
#: pretix/static/pretixbase/js/asynctask.js:342
|
||||||
msgid "Close message"
|
msgid "Close message"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -261,6 +428,165 @@ msgstr ""
|
|||||||
msgid "Press Ctrl-C to copy!"
|
msgid "Press Ctrl-C to copy!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/App.vue:80
|
||||||
|
msgid "Edit"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/App.vue:86
|
||||||
|
msgid "Visualize"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/App.vue:96
|
||||||
|
msgid ""
|
||||||
|
"Your rule always filters by product or variation, but the following products "
|
||||||
|
"or variations are not contained in any of your rule parts so people with "
|
||||||
|
"these tickets will not get in:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/App.vue:99
|
||||||
|
msgid "Please double-check if this was intentional."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:4
|
||||||
|
msgid "All of the conditions below (AND)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:5
|
||||||
|
msgid "At least one of the conditions below (OR)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:6
|
||||||
|
msgid "Event start"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:7
|
||||||
|
msgid "Event end"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:8
|
||||||
|
msgid "Event admission"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:9
|
||||||
|
msgid "custom date and time"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:10
|
||||||
|
msgid "custom time"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:11
|
||||||
|
msgid "Tolerance (minutes)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:12
|
||||||
|
msgid "Add condition"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:13
|
||||||
|
msgid "minutes"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:14
|
||||||
|
msgid "Duplicate"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:15
|
||||||
|
msgctxt "entry_status"
|
||||||
|
msgid "present"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:16
|
||||||
|
msgctxt "entry_status"
|
||||||
|
msgid "absent"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:29
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:35
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:41
|
||||||
|
msgid "is one of"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:47
|
||||||
|
msgid "is before"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:51
|
||||||
|
msgid "is after"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:57
|
||||||
|
msgid "="
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:117
|
||||||
|
msgid "Product"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:121
|
||||||
|
msgid "Product variation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:125
|
||||||
|
msgid "Gate"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:129
|
||||||
|
msgid "Current date and time"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:133
|
||||||
|
msgid "Current day of the week (1 = Monday, 7 = Sunday)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:137
|
||||||
|
msgid "Current entry status"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:141
|
||||||
|
msgid "Number of previous entries"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:145
|
||||||
|
msgid "Number of previous entries since midnight"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:149
|
||||||
|
msgid "Number of previous entries since"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:153
|
||||||
|
msgid "Number of previous entries before"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:157
|
||||||
|
msgid "Number of days with a previous entry"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:161
|
||||||
|
msgid "Number of days with a previous entry since"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:165
|
||||||
|
msgid "Number of days with a previous entry before"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:169
|
||||||
|
msgid "Minutes since last entry (-1 on first entry)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/constants.ts:173
|
||||||
|
msgid "Minutes since first entry (-1 on first entry)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/django-interop.ts:47
|
||||||
|
msgid "Error: Product not found!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pretix/static/pretixcontrol/js/ui/checkinrules/django-interop.ts:54
|
||||||
|
msgid "Error: Variation not found!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/editor.js:171
|
#: pretix/static/pretixcontrol/js/ui/editor.js:171
|
||||||
msgid "Check-in QR"
|
msgid "Check-in QR"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -359,23 +685,23 @@ msgstr ""
|
|||||||
msgid "Selected only"
|
msgid "Selected only"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:842
|
#: pretix/static/pretixcontrol/js/ui/main.js:844
|
||||||
msgid "Enter page number between 1 and %(max)s."
|
msgid "Enter page number between 1 and %(max)s."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:845
|
#: pretix/static/pretixcontrol/js/ui/main.js:847
|
||||||
msgid "Invalid page number."
|
msgid "Invalid page number."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:1003
|
#: pretix/static/pretixcontrol/js/ui/main.js:1005
|
||||||
msgid "Use a different name internally"
|
msgid "Use a different name internally"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:1043
|
#: pretix/static/pretixcontrol/js/ui/main.js:1045
|
||||||
msgid "Click to close"
|
msgid "Click to close"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/main.js:1124
|
#: pretix/static/pretixcontrol/js/ui/main.js:1126
|
||||||
msgid "You have unsaved changes!"
|
msgid "You have unsaved changes!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -395,16 +721,6 @@ msgstr ""
|
|||||||
msgid "Count"
|
msgid "Count"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/question.js:136
|
|
||||||
#: pretix/static/pretixpresale/js/ui/questions.js:271
|
|
||||||
msgid "Yes"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/question.js:137
|
|
||||||
#: pretix/static/pretixpresale/js/ui/questions.js:271
|
|
||||||
msgid "No"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: pretix/static/pretixcontrol/js/ui/subevent.js:112
|
#: pretix/static/pretixcontrol/js/ui/subevent.js:112
|
||||||
msgid "(one more date)"
|
msgid "(one more date)"
|
||||||
msgid_plural "({num} more dates)"
|
msgid_plural "({num} more dates)"
|
||||||
@@ -471,164 +787,196 @@ msgid "Google Pay"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:16
|
#: pretix/static/pretixpresale/js/widget/widget.js:16
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:30
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Quantity"
|
msgid "Quantity"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:31
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Decrease quantity"
|
msgid "Decrease quantity"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:18
|
#: pretix/static/pretixpresale/js/widget/widget.js:18
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:32
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Increase quantity"
|
msgid "Increase quantity"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:19
|
#: pretix/static/pretixpresale/js/widget/widget.js:19
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:33
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Filter events by"
|
msgid "Filter events by"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:20
|
#: pretix/static/pretixpresale/js/widget/widget.js:20
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:34
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Filter"
|
msgid "Filter"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:21
|
#: pretix/static/pretixpresale/js/widget/widget.js:21
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:35
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Price"
|
msgid "Price"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:22
|
#: pretix/static/pretixpresale/js/widget/widget.js:22
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:36
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Original price: %s"
|
msgid "Original price: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:23
|
#: pretix/static/pretixpresale/js/widget/widget.js:23
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:37
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "New price: %s"
|
msgid "New price: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:24
|
#: pretix/static/pretixpresale/js/widget/widget.js:24
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:38
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Select"
|
msgid "Select"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:25
|
#: pretix/static/pretixpresale/js/widget/widget.js:25
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:39
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Select %s"
|
msgid "Select %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:26
|
#: pretix/static/pretixpresale/js/widget/widget.js:26
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:40
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Select variant %s"
|
msgid "Select variant %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:27
|
#: pretix/static/pretixpresale/js/widget/widget.js:27
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:41
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Sold out"
|
msgid "Sold out"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:28
|
#: pretix/static/pretixpresale/js/widget/widget.js:28
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:42
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Buy"
|
msgid "Buy"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:29
|
#: pretix/static/pretixpresale/js/widget/widget.js:29
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:43
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Register"
|
msgid "Register"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:30
|
#: pretix/static/pretixpresale/js/widget/widget.js:30
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:44
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Reserved"
|
msgid "Reserved"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:31
|
#: pretix/static/pretixpresale/js/widget/widget.js:31
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:45
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "FREE"
|
msgid "FREE"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:32
|
#: pretix/static/pretixpresale/js/widget/widget.js:32
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:46
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "from %(currency)s %(price)s"
|
msgid "from %(currency)s %(price)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:33
|
#: pretix/static/pretixpresale/js/widget/widget.js:33
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:47
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Image of %s"
|
msgid "Image of %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:34
|
#: pretix/static/pretixpresale/js/widget/widget.js:34
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:48
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "incl. %(rate)s% %(taxname)s"
|
msgid "incl. %(rate)s% %(taxname)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:35
|
#: pretix/static/pretixpresale/js/widget/widget.js:35
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:49
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "plus %(rate)s% %(taxname)s"
|
msgid "plus %(rate)s% %(taxname)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:36
|
#: pretix/static/pretixpresale/js/widget/widget.js:36
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:50
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "incl. taxes"
|
msgid "incl. taxes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:37
|
#: pretix/static/pretixpresale/js/widget/widget.js:37
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:51
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "plus taxes"
|
msgid "plus taxes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:38
|
#: pretix/static/pretixpresale/js/widget/widget.js:38
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:52
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "currently available: %s"
|
msgid "currently available: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:39
|
#: pretix/static/pretixpresale/js/widget/widget.js:39
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:53
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Only available with a voucher"
|
msgid "Only available with a voucher"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:40
|
#: pretix/static/pretixpresale/js/widget/widget.js:40
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:43
|
#: pretix/static/pretixpresale/js/widget/widget.js:43
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:54
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:57
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Not yet available"
|
msgid "Not yet available"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:41
|
#: pretix/static/pretixpresale/js/widget/widget.js:41
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:55
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Not available anymore"
|
msgid "Not available anymore"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:42
|
#: pretix/static/pretixpresale/js/widget/widget.js:42
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:56
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Currently not available"
|
msgid "Currently not available"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:44
|
#: pretix/static/pretixpresale/js/widget/widget.js:44
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:58
|
||||||
#, javascript-format
|
#, javascript-format
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "minimum amount to order: %s"
|
msgid "minimum amount to order: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:45
|
#: pretix/static/pretixpresale/js/widget/widget.js:45
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:59
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Close ticket shop"
|
msgid "Close ticket shop"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:46
|
#: pretix/static/pretixpresale/js/widget/widget.js:46
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:60
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "The ticket shop could not be loaded."
|
msgid "The ticket shop could not be loaded."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:47
|
#: pretix/static/pretixpresale/js/widget/widget.js:47
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:61
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid ""
|
msgid ""
|
||||||
"There are currently a lot of users in this ticket shop. Please open the shop "
|
"There are currently a lot of users in this ticket shop. Please open the shop "
|
||||||
@@ -636,21 +984,25 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:49
|
#: pretix/static/pretixpresale/js/widget/widget.js:49
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:62
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Open ticket shop"
|
msgid "Open ticket shop"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:50
|
#: pretix/static/pretixpresale/js/widget/widget.js:50
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:63
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Checkout"
|
msgid "Checkout"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:51
|
#: pretix/static/pretixpresale/js/widget/widget.js:51
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:64
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "The cart could not be created. Please try again later"
|
msgid "The cart could not be created. Please try again later"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:52
|
#: pretix/static/pretixpresale/js/widget/widget.js:52
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:65
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid ""
|
msgid ""
|
||||||
"We could not create your cart, since there are currently too many users in "
|
"We could not create your cart, since there are currently too many users in "
|
||||||
@@ -658,11 +1010,13 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:54
|
#: pretix/static/pretixpresale/js/widget/widget.js:54
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:66
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Waiting list"
|
msgid "Waiting list"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:55
|
#: pretix/static/pretixpresale/js/widget/widget.js:55
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:67
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid ""
|
msgid ""
|
||||||
"You currently have an active cart for this event. If you select more "
|
"You currently have an active cart for this event. If you select more "
|
||||||
@@ -670,96 +1024,115 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:57
|
#: pretix/static/pretixpresale/js/widget/widget.js:57
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:68
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Resume checkout"
|
msgid "Resume checkout"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:58
|
#: pretix/static/pretixpresale/js/widget/widget.js:58
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:69
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Redeem a voucher"
|
msgid "Redeem a voucher"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:59
|
#: pretix/static/pretixpresale/js/widget/widget.js:59
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:70
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Redeem"
|
msgid "Redeem"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:60
|
#: pretix/static/pretixpresale/js/widget/widget.js:60
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:71
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Voucher code"
|
msgid "Voucher code"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:61
|
#: pretix/static/pretixpresale/js/widget/widget.js:61
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:72
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Close"
|
msgid "Close"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:62
|
#: pretix/static/pretixpresale/js/widget/widget.js:62
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:73
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Close checkout"
|
msgid "Close checkout"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:63
|
#: pretix/static/pretixpresale/js/widget/widget.js:63
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:74
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "You cannot cancel this operation. Please wait for loading to finish."
|
msgid "You cannot cancel this operation. Please wait for loading to finish."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:64
|
#: pretix/static/pretixpresale/js/widget/widget.js:64
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:75
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Continue"
|
msgid "Continue"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:65
|
#: pretix/static/pretixpresale/js/widget/widget.js:65
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:76
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Show variants"
|
msgid "Show variants"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:66
|
#: pretix/static/pretixpresale/js/widget/widget.js:66
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:77
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Hide variants"
|
msgid "Hide variants"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:67
|
#: pretix/static/pretixpresale/js/widget/widget.js:67
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:78
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Choose a different event"
|
msgid "Choose a different event"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:68
|
#: pretix/static/pretixpresale/js/widget/widget.js:68
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:79
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Choose a different date"
|
msgid "Choose a different date"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:69
|
#: pretix/static/pretixpresale/js/widget/widget.js:69
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:80
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Back"
|
msgid "Back"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:70
|
#: pretix/static/pretixpresale/js/widget/widget.js:70
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:81
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Next month"
|
msgid "Next month"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:71
|
#: pretix/static/pretixpresale/js/widget/widget.js:71
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:82
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Previous month"
|
msgid "Previous month"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:72
|
#: pretix/static/pretixpresale/js/widget/widget.js:72
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:83
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Next week"
|
msgid "Next week"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:73
|
#: pretix/static/pretixpresale/js/widget/widget.js:73
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:84
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Previous week"
|
msgid "Previous week"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:74
|
#: pretix/static/pretixpresale/js/widget/widget.js:74
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:85
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Open seat selection"
|
msgid "Open seat selection"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:75
|
#: pretix/static/pretixpresale/js/widget/widget.js:75
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:86
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid ""
|
msgid ""
|
||||||
"Some or all ticket categories are currently sold out. If you want, you can "
|
"Some or all ticket categories are currently sold out. If you want, you can "
|
||||||
@@ -768,110 +1141,137 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:76
|
#: pretix/static/pretixpresale/js/widget/widget.js:76
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:87
|
||||||
msgctxt "widget"
|
msgctxt "widget"
|
||||||
msgid "Load more"
|
msgid "Load more"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:78
|
#: pretix/static/pretixpresale/js/widget/widget.js:78
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:89
|
||||||
msgid "Mo"
|
msgid "Mo"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:79
|
#: pretix/static/pretixpresale/js/widget/widget.js:79
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:90
|
||||||
msgid "Tu"
|
msgid "Tu"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:80
|
#: pretix/static/pretixpresale/js/widget/widget.js:80
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:91
|
||||||
msgid "We"
|
msgid "We"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:81
|
#: pretix/static/pretixpresale/js/widget/widget.js:81
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:92
|
||||||
msgid "Th"
|
msgid "Th"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:82
|
#: pretix/static/pretixpresale/js/widget/widget.js:82
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:93
|
||||||
msgid "Fr"
|
msgid "Fr"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:83
|
#: pretix/static/pretixpresale/js/widget/widget.js:83
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:94
|
||||||
msgid "Sa"
|
msgid "Sa"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:84
|
#: pretix/static/pretixpresale/js/widget/widget.js:84
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:95
|
||||||
msgid "Su"
|
msgid "Su"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:85
|
#: pretix/static/pretixpresale/js/widget/widget.js:85
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:96
|
||||||
msgid "Monday"
|
msgid "Monday"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:86
|
#: pretix/static/pretixpresale/js/widget/widget.js:86
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:97
|
||||||
msgid "Tuesday"
|
msgid "Tuesday"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:87
|
#: pretix/static/pretixpresale/js/widget/widget.js:87
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:98
|
||||||
msgid "Wednesday"
|
msgid "Wednesday"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:88
|
#: pretix/static/pretixpresale/js/widget/widget.js:88
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:99
|
||||||
msgid "Thursday"
|
msgid "Thursday"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:89
|
#: pretix/static/pretixpresale/js/widget/widget.js:89
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:100
|
||||||
msgid "Friday"
|
msgid "Friday"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:90
|
#: pretix/static/pretixpresale/js/widget/widget.js:90
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:101
|
||||||
msgid "Saturday"
|
msgid "Saturday"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:91
|
#: pretix/static/pretixpresale/js/widget/widget.js:91
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:102
|
||||||
msgid "Sunday"
|
msgid "Sunday"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:94
|
#: pretix/static/pretixpresale/js/widget/widget.js:94
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:105
|
||||||
msgid "January"
|
msgid "January"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:95
|
#: pretix/static/pretixpresale/js/widget/widget.js:95
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:106
|
||||||
msgid "February"
|
msgid "February"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:96
|
#: pretix/static/pretixpresale/js/widget/widget.js:96
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:107
|
||||||
msgid "March"
|
msgid "March"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:97
|
#: pretix/static/pretixpresale/js/widget/widget.js:97
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:108
|
||||||
msgid "April"
|
msgid "April"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:98
|
#: pretix/static/pretixpresale/js/widget/widget.js:98
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:109
|
||||||
msgid "May"
|
msgid "May"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:99
|
#: pretix/static/pretixpresale/js/widget/widget.js:99
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:110
|
||||||
msgid "June"
|
msgid "June"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:100
|
#: pretix/static/pretixpresale/js/widget/widget.js:100
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:111
|
||||||
msgid "July"
|
msgid "July"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:101
|
#: pretix/static/pretixpresale/js/widget/widget.js:101
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:112
|
||||||
msgid "August"
|
msgid "August"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:102
|
#: pretix/static/pretixpresale/js/widget/widget.js:102
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:113
|
||||||
msgid "September"
|
msgid "September"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:103
|
#: pretix/static/pretixpresale/js/widget/widget.js:103
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:114
|
||||||
msgid "October"
|
msgid "October"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:104
|
#: pretix/static/pretixpresale/js/widget/widget.js:104
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:115
|
||||||
msgid "November"
|
msgid "November"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pretix/static/pretixpresale/js/widget/widget.js:105
|
#: pretix/static/pretixpresale/js/widget/widget.js:105
|
||||||
|
#: pretix/static/pretixpresale/widget/src/i18n.ts:116
|
||||||
msgid "December"
|
msgid "December"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
+2292
-2096
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