mirror of
https://github.com/pretix/pretix.git
synced 2026-10-03 19:34:41 +00:00
Compare commits
218
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7abf79f2db | ||
|
|
0ef196d622 | ||
|
|
84858bc048 | ||
|
|
e8c091741b | ||
|
|
ebdfd21b0f | ||
|
|
ed0b3cab7f | ||
|
|
3dd8cc5862 | ||
|
|
23e9ca8f34 | ||
|
|
e19afd39fb | ||
|
|
f7863c9fee | ||
|
|
fc3150fecc | ||
|
|
89318bcc24 | ||
|
|
a388391f06 | ||
|
|
b4d000379c | ||
|
|
ba9bba49c6 | ||
|
|
824624cce6 | ||
|
|
8601f62581 | ||
|
|
7547d64dea | ||
|
|
d1186c83ef | ||
|
|
e4782ff217 | ||
|
|
3328dc28c7 | ||
|
|
6c83b4288a | ||
|
|
6ff37796c3 | ||
|
|
fa60c320b9 | ||
|
|
ca9d9faef4 | ||
|
|
9718d1ee83 | ||
|
|
7bdb2c1555 | ||
|
|
da6753b53f | ||
|
|
33df9d13be | ||
|
|
582f9ac642 | ||
|
|
5ce2957965 | ||
|
|
eb9d6ad9a3 | ||
|
|
d339e1d594 | ||
|
|
cb15559ac6 | ||
|
|
3c95f1fee7 | ||
|
|
6a380c9356 | ||
|
|
eeea01b31a | ||
|
|
470621b5cb | ||
|
|
5802153101 | ||
|
|
e9f89d5d92 | ||
|
|
695689ed06 | ||
|
|
12d405d861 | ||
|
|
649eee3025 | ||
|
|
149b4f23aa | ||
|
|
fa40ccc623 | ||
|
|
2ba74b1697 | ||
|
|
e54cd6af44 | ||
|
|
d2b58d428e | ||
|
|
74332faa7b | ||
|
|
bbf391f7d5 | ||
|
|
d78d5b52fa | ||
|
|
9c0fef3d72 | ||
|
|
c7d6630979 | ||
|
|
4edd3c4654 | ||
|
|
b1ae638394 | ||
|
|
ae9bb68645 | ||
|
|
48f5a7c8cc | ||
|
|
d43032274b | ||
|
|
ae9a744fd9 | ||
|
|
ff4d3cd403 | ||
|
|
3440ee16f2 | ||
|
|
43e5b62db3 | ||
|
|
b249c7b417 | ||
|
|
b68c324389 | ||
|
|
4b320b6dab | ||
|
|
df74cbf5fc | ||
|
|
c875d758f9 | ||
|
|
2ebdd048c2 | ||
|
|
ca40b09080 | ||
|
|
558bf910fd | ||
|
|
7e1e472691 | ||
|
|
b06c9ef463 | ||
|
|
2af8d29ca9 | ||
|
|
2791d5e49e | ||
|
|
ed68719731 | ||
|
|
9aeb4b9731 | ||
|
|
9324887790 | ||
|
|
d353384555 | ||
|
|
b8f8e49cce | ||
|
|
806d0a5748 | ||
|
|
69e541e5af | ||
|
|
0723e6015c | ||
|
|
b8e1ab8258 | ||
|
|
81764278ae | ||
|
|
f3068e627a | ||
|
|
993a3f96e9 | ||
|
|
1af0b5e442 | ||
|
|
71b6bf630f | ||
|
|
59026da06b | ||
|
|
b3e0892d76 | ||
|
|
3029a6839b | ||
|
|
a826b0a1bd | ||
|
|
7d7474c07d | ||
|
|
58023381b9 | ||
|
|
e5c44e7aed | ||
|
|
d77a179606 | ||
|
|
29614db1e2 | ||
|
|
365051cc21 | ||
|
|
5c6b0eef6f | ||
|
|
bc0a6b662b | ||
|
|
0bc1aed1a0 | ||
|
|
0eaa5a081c | ||
|
|
688c434c4f | ||
|
|
c0474604a1 | ||
|
|
1741d08c2c | ||
|
|
6bbd808aaa | ||
|
|
aa90c5b3b1 | ||
|
|
bfbabe0e58 | ||
|
|
aa14505d2c | ||
|
|
df69656364 | ||
|
|
82cffd1519 | ||
|
|
7fc527135d | ||
|
|
8e39b67ee9 | ||
|
|
289cbe5bfc | ||
|
|
7143b877c8 | ||
|
|
e2a78100af | ||
|
|
ca77467e48 | ||
|
|
d3cbfcd4da | ||
|
|
3648ef28d7 | ||
|
|
689b99c3aa | ||
|
|
6c40daa863 | ||
|
|
efdc83f72a | ||
|
|
8b10534c74 | ||
|
|
bcacff2ee7 | ||
|
|
47096f7f1b | ||
|
|
c240ebcb8e | ||
|
|
ce3e59e020 | ||
|
|
4a9da40028 | ||
|
|
eab1efd5f4 | ||
|
|
c7290b614e | ||
|
|
1aea5633eb | ||
|
|
86cc719651 | ||
|
|
742a69f09f | ||
|
|
c05dee7af5 | ||
|
|
e399a49c8b | ||
|
|
b9b951e56b | ||
|
|
88bb1f4008 | ||
|
|
24e7db7ddd | ||
|
|
96ed6fe1a6 | ||
|
|
d85e52c83e | ||
|
|
d9b5fa5b16 | ||
|
|
fb948cc7f4 | ||
|
|
c358f412fd | ||
|
|
b78de6386b | ||
|
|
50c00b47ae | ||
|
|
6025c7942f | ||
|
|
23a3412154 | ||
|
|
3635f6fb0c | ||
|
|
2aadfa2ce4 | ||
|
|
dd0d78242e | ||
|
|
42c5de895c | ||
|
|
e91718e73b | ||
|
|
2c15d8b074 | ||
|
|
edb4069e18 | ||
|
|
dc7d5c6029 | ||
|
|
caa6fb187b | ||
|
|
7d93cae2a9 | ||
|
|
58a58eff83 | ||
|
|
a84a02c298 | ||
|
|
4390403b9a | ||
|
|
9d53cf840b | ||
|
|
023f9104ef | ||
|
|
e6572344ca | ||
|
|
2e8e6a6b07 | ||
|
|
19cd0a7f43 | ||
|
|
aa583a291f | ||
|
|
1414c22eb6 | ||
|
|
0a6b8c0493 | ||
|
|
4b4a301e6e | ||
|
|
d7aae65777 | ||
|
|
dbd971cc22 | ||
|
|
6d4aba6e3d | ||
|
|
4755e7af0e | ||
|
|
f476ce7696 | ||
|
|
bf85789efd | ||
|
|
3fbd63e80a | ||
|
|
2af60b499f | ||
|
|
b74371bef1 | ||
|
|
9f8ba28cda | ||
|
|
44a7bfceab | ||
|
|
5af9be6ef0 | ||
|
|
b809d93bdc | ||
|
|
d5a8d71ed3 | ||
|
|
d481574d42 | ||
|
|
0593172146 | ||
|
|
64a5361dba | ||
|
|
5622c1ddaa | ||
|
|
73c29fc3b9 | ||
|
|
c73f2f77cf | ||
|
|
55a0db3a32 | ||
|
|
a47768312b | ||
|
|
cf272bf575 | ||
|
|
f3e0e7f4ac | ||
|
|
5a06f9416c | ||
|
|
606e3f1c0f | ||
|
|
e38ae32028 | ||
|
|
b74e2fa3e1 | ||
|
|
c79a7002a6 | ||
|
|
1ebbae2b5b | ||
|
|
7db3b47889 | ||
|
|
b7ac8fc4ef | ||
|
|
c1a830e54d | ||
|
|
2178dee6db | ||
|
|
266104f4af | ||
|
|
d10afbd080 | ||
|
|
1e78bfbc4c | ||
|
|
08356feea1 | ||
|
|
b124cb6aad | ||
|
|
cb5182f21c | ||
|
|
011487404c | ||
|
|
7703c04954 | ||
|
|
58f331ba1f | ||
|
|
91a3993cef | ||
|
|
4969c12c0a | ||
|
|
0b319d201c | ||
|
|
eceeb9caa7 | ||
|
|
3c17d05d05 | ||
|
|
1c17c35b16 |
@@ -0,0 +1,2 @@
|
||||
# Format pre-vue code with eslint where possible (2026-09-10)
|
||||
d85e52c83ed3639e040372fd0052e842e4899d90
|
||||
@@ -0,0 +1,43 @@
|
||||
name: SBOM
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, sbom ]
|
||||
tags: [ 'v.*' ]
|
||||
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-22.04
|
||||
name: Submission
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.13"
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24.x'
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-
|
||||
- name: Install system dependencies
|
||||
run: sudo apt update && sudo apt install -y gettext unzip
|
||||
- name: Install Python dependencies
|
||||
run: pip3 install -U "prisma-sbom-submit[python]"
|
||||
- name: Create SBOM
|
||||
run: NPM=$(which npm) prisma-sbom-submit collect . sbom.json
|
||||
- name: Submit SBOM
|
||||
run: prisma-sbom-submit upload --server https://prisma.pretix.com sbom.json
|
||||
env:
|
||||
PRISMA_UPLOAD_TOKEN: ${{ secrets.PRISMA_UPLOAD_TOKEN }}
|
||||
@@ -123,7 +123,24 @@ jobs:
|
||||
working-directory: ./src
|
||||
run: make all compress
|
||||
- name: Install Playwright browsers
|
||||
run: playwright install
|
||||
run: playwright install --with-deps
|
||||
- name: Run E2E tests
|
||||
working-directory: ./src
|
||||
run: PRETIX_CONFIG_FILE=tests/ci_postgres.cfg py.test tests/e2e/ -v --maxfail=10
|
||||
run: PRETIX_CONFIG_FILE=tests/ci_postgres.cfg py.test tests/e2e/ -v --maxfail=10 --tracing=retain-on-failure
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ !cancelled() }}
|
||||
with:
|
||||
name: playwright-traces
|
||||
path: test-results/
|
||||
- name: Log trace instructions
|
||||
if: steps.check-traces.outputs.found == 'true'
|
||||
run: |
|
||||
{
|
||||
echo "## 🎭 Playwright traces available"
|
||||
echo ""
|
||||
echo "Some tests failed or retried and produced traces."
|
||||
echo ""
|
||||
echo "1. Download the **playwright-traces-${{ github.run_id }}** artifact from this run (link in the **Summary** tab, under Artifacts)."
|
||||
echo "2. Unzip it."
|
||||
echo "3. Go to https://trace.playwright.dev and drag \`trace.zip\` into the page — or run \`npx playwright show-trace trace.zip\` locally."
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
+1
-2
@@ -16,8 +16,6 @@ recursive-include src/pretix/plugins/banktransfer/templates *
|
||||
recursive-include src/pretix/plugins/banktransfer/static *
|
||||
recursive-include src/pretix/plugins/manualpayment/templates *
|
||||
recursive-include src/pretix/plugins/manualpayment/static *
|
||||
recursive-include src/pretix/plugins/paypal/templates *
|
||||
recursive-include src/pretix/plugins/paypal/static *
|
||||
recursive-include src/pretix/plugins/paypal2/templates *
|
||||
recursive-include src/pretix/plugins/paypal2/static *
|
||||
recursive-include src/pretix/plugins/src/pretixdroid/templates *
|
||||
@@ -44,6 +42,7 @@ recursive-include src *.py
|
||||
recursive-include src *.svg
|
||||
recursive-include src *.txt
|
||||
recursive-include src Makefile
|
||||
recursive-include src *.pdf
|
||||
|
||||
recursive-exclude doc *
|
||||
recursive-exclude deployment *
|
||||
|
||||
@@ -53,6 +53,8 @@ Checking a ticket in
|
||||
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
|
||||
``Accept-Language`` header).
|
||||
:<json boolean simulate: Do not actually perform the check-in, only simulate the response. The ``position`` response
|
||||
object will not reflect the simulated changes.
|
||||
:>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_explanation: Human-readable explanation, only set on status ``"error"`` and reason ``"rules"``, can be null.
|
||||
@@ -71,8 +73,6 @@ Checking a ticket in
|
||||
:>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"``.
|
||||
:>json boolean simulate: Do not actually perform the check-in, only simulate the response. The ``position`` response
|
||||
object will not reflect the simulated changes.
|
||||
|
||||
**Example request**:
|
||||
|
||||
|
||||
@@ -0,0 +1,241 @@
|
||||
Event Meta Properties
|
||||
=====================
|
||||
|
||||
Resource description
|
||||
--------------------
|
||||
|
||||
An event meta property is used to to define meta information fields for its events.
|
||||
This information can be re-used, for example, in ticket layouts.
|
||||
|
||||
The event meta property resource contains the following public fields:
|
||||
|
||||
.. rst-class:: rest-resource-table
|
||||
|
||||
===================================== ========================== =======================================================
|
||||
Field Type Description
|
||||
===================================== ========================== =======================================================
|
||||
id integer Unique ID for this property
|
||||
name string Name of the property
|
||||
default string Value of the default option
|
||||
required boolean If ``true``, an event can only be taken live if the
|
||||
property is set. In event series, it's always optional
|
||||
to set a value for individual dates
|
||||
protected boolean If ``true``, the value for an event can only be changed
|
||||
by organizer-level administrators
|
||||
filter_public boolean If ``true``, this property will be shown to filter
|
||||
events in the public event list and calendar
|
||||
public_label string Public name of the property
|
||||
filter_allowed boolean If ``true``, this property will be shown to filter
|
||||
events or reports in the backend, and it can also be
|
||||
used for hidden filter parameters in the frontend
|
||||
choices list of objects List of JSON objects representing all permitted values
|
||||
for this property, or ``null`` for no limitation.
|
||||
Each choice object has a required internal name named
|
||||
``key`` and optional public name named ``label``
|
||||
consisting of a dictionary of i18n string translations
|
||||
===================================== ========================== =======================================================
|
||||
|
||||
Endpoints
|
||||
---------
|
||||
|
||||
.. http:get:: /api/v1/organizers/(organizer)/event_meta_properties/
|
||||
|
||||
Returns a list of all meta properties for the organizer.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/v1/organizers/bigevents/meta_properties/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"count": 1,
|
||||
"next": null,
|
||||
"previous": null,
|
||||
"results": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Color",
|
||||
"default": "blue",
|
||||
"required": false,
|
||||
"protected": false,
|
||||
"filter_public": false,
|
||||
"public_label": {},
|
||||
"filter_allowed": true,
|
||||
"choices": [
|
||||
{
|
||||
"key": "blue",
|
||||
"label": {
|
||||
"en": "Blue"
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer
|
||||
:statuscode 200: no error
|
||||
:statuscode 401: Authentication failure
|
||||
:statuscode 403: The requested organizer does not exist **or** you have no permission to view this resource.
|
||||
|
||||
.. http:get:: /api/v1/organizers/(organizer)/event_meta_properties/(id)/
|
||||
|
||||
Returns information on one property, identified by its id.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/v1/organizers/bigevents/event_meta_properties/1/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Color",
|
||||
"default": "blue",
|
||||
"required": false,
|
||||
"protected": false,
|
||||
"filter_public": false,
|
||||
"public_label": {},
|
||||
"filter_allowed": true,
|
||||
"choices": null
|
||||
}
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer
|
||||
:param id: The ``id`` field of the meta property to retrieve
|
||||
:statuscode 200: no error
|
||||
:statuscode 401: Authentication failure
|
||||
:statuscode 403: The requested organizer does not exist **or** you have no permission to view this resource.
|
||||
|
||||
.. http:post:: /api/v1/organizers/(organizer)/event_meta_properties/
|
||||
|
||||
Creates a new meta property
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /api/v1/organizers/bigevents/event_meta_properties/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "ref-code",
|
||||
"default": "abcde",
|
||||
"required": true,
|
||||
"choices": null
|
||||
}
|
||||
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
{
|
||||
"id": 2,
|
||||
"name": "reference",
|
||||
"default": "abcde",
|
||||
"required": true,
|
||||
"protected": false,
|
||||
"filter_public": false,
|
||||
"public_label": null,
|
||||
"filter_allowed": true,
|
||||
"choices": null
|
||||
}
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer
|
||||
:statuscode 201: no error
|
||||
:statuscode 400: The meta property could not be created due to invalid submitted data.
|
||||
:statuscode 401: Authentication failure
|
||||
:statuscode 403: The requested organizer does not exist **or** you have no permission to create this resource.
|
||||
|
||||
.. http:patch:: /api/v1/organizers/(organizer)/event_meta_properties/(id)/
|
||||
|
||||
Update a meta property. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide
|
||||
all fields of the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the
|
||||
fields that you want to change.
|
||||
|
||||
You can change all fields of the resource except the ``id`` field.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
PATCH /api/v1/organizers/bigevents/event_meta_properties/2/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
Content-Type: application/json
|
||||
Content-Length: 94
|
||||
|
||||
{
|
||||
"required": false
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": 3,
|
||||
"name": "reference",
|
||||
"default": "abcde",
|
||||
"required": false,
|
||||
"protected": false,
|
||||
"filter_public": false,
|
||||
"public_label": null,
|
||||
"filter_allowed": true,
|
||||
"choices": null
|
||||
}
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer
|
||||
:param id: The ``id`` field of the meta property to modify
|
||||
:statuscode 200: no error
|
||||
:statuscode 400: The property could not be modified due to invalid submitted data
|
||||
:statuscode 401: Authentication failure
|
||||
:statuscode 403: The requested organizer does not exist **or** you have no permission to change this resource.
|
||||
|
||||
.. http:delete:: /api/v1/organizers/(organizer)/event_meta_properties/(id)/
|
||||
|
||||
Delete a meta property.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
DELETE /api/v1/organizers/bigevents/event_meta_properties/1/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 204 No Content
|
||||
Vary: Accept
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer
|
||||
:param id: The ``id`` field of the meta property to delete
|
||||
:statuscode 204: no error
|
||||
:statuscode 401: Authentication failure
|
||||
:statuscode 403: The requested organizer does not exist **or** you have no permission to delete this resource.
|
||||
@@ -110,7 +110,7 @@ Endpoints
|
||||
"plugins": [
|
||||
"pretix.plugins.banktransfer",
|
||||
"pretix.plugins.stripe",
|
||||
"pretix.plugins.paypal",
|
||||
"pretix.plugins.paypal2",
|
||||
"pretix.plugins.ticketoutputpdf"
|
||||
],
|
||||
"all_sales_channels": false,
|
||||
@@ -199,7 +199,7 @@ Endpoints
|
||||
"plugins": [
|
||||
"pretix.plugins.banktransfer",
|
||||
"pretix.plugins.stripe",
|
||||
"pretix.plugins.paypal",
|
||||
"pretix.plugins.paypal2",
|
||||
"pretix.plugins.ticketoutputpdf"
|
||||
],
|
||||
"valid_keys": {
|
||||
@@ -262,7 +262,7 @@ Endpoints
|
||||
"item_meta_properties": {},
|
||||
"plugins": [
|
||||
"pretix.plugins.stripe",
|
||||
"pretix.plugins.paypal"
|
||||
"pretix.plugins.paypal2"
|
||||
],
|
||||
"all_sales_channels": true,
|
||||
"limit_sales_channels": []
|
||||
@@ -299,7 +299,7 @@ Endpoints
|
||||
"item_meta_properties": {},
|
||||
"plugins": [
|
||||
"pretix.plugins.stripe",
|
||||
"pretix.plugins.paypal"
|
||||
"pretix.plugins.paypal2"
|
||||
],
|
||||
"all_sales_channels": true,
|
||||
"limit_sales_channels": [],
|
||||
@@ -364,7 +364,7 @@ Endpoints
|
||||
"item_meta_properties": {},
|
||||
"plugins": [
|
||||
"pretix.plugins.stripe",
|
||||
"pretix.plugins.paypal"
|
||||
"pretix.plugins.paypal2"
|
||||
],
|
||||
"all_sales_channels": true,
|
||||
"limit_sales_channels": []
|
||||
@@ -401,7 +401,7 @@ Endpoints
|
||||
"item_meta_properties": {},
|
||||
"plugins": [
|
||||
"pretix.plugins.stripe",
|
||||
"pretix.plugins.paypal"
|
||||
"pretix.plugins.paypal2"
|
||||
],
|
||||
"all_sales_channels": true,
|
||||
"limit_sales_channels": [],
|
||||
@@ -438,7 +438,7 @@ Endpoints
|
||||
"plugins": [
|
||||
"pretix.plugins.banktransfer",
|
||||
"pretix.plugins.stripe",
|
||||
"pretix.plugins.paypal",
|
||||
"pretix.plugins.paypal2",
|
||||
"pretix.plugins.pretixdroid"
|
||||
]
|
||||
}
|
||||
@@ -475,7 +475,7 @@ Endpoints
|
||||
"plugins": [
|
||||
"pretix.plugins.banktransfer",
|
||||
"pretix.plugins.stripe",
|
||||
"pretix.plugins.paypal",
|
||||
"pretix.plugins.paypal2",
|
||||
"pretix.plugins.pretixdroid"
|
||||
],
|
||||
"all_sales_channels": true,
|
||||
@@ -566,7 +566,7 @@ organizer level.
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"imprint_url": "https://pretix.eu",
|
||||
"region": "DE",
|
||||
…
|
||||
}
|
||||
|
||||
@@ -579,12 +579,14 @@ organizer level.
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"imprint_url":
|
||||
"region":
|
||||
{
|
||||
"value": "https://pretix.eu",
|
||||
"label": "Imprint URL",
|
||||
"value": "DE",
|
||||
"label": "Region",
|
||||
"readonly": false,
|
||||
"help_text": "This should point e.g. to a part of your website that has your contact details and legal information."
|
||||
"help_text": "Will be used to determine date and time formatting as well as default country for customer
|
||||
addresses and phone numbers. For formatting, this takes less priority than the language and
|
||||
is therefore mostly relevant for languages used in different regions globally (like English)."
|
||||
}
|
||||
},
|
||||
…
|
||||
@@ -620,7 +622,7 @@ organizer level.
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"imprint_url": "https://example.org/imprint/"
|
||||
"region": "DE"
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
@@ -632,7 +634,7 @@ organizer level.
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"imprint_url": "https://example.org/imprint/",
|
||||
"region": "DE",
|
||||
…
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ at :ref:`plugin-docs`.
|
||||
organizers
|
||||
events
|
||||
subevents
|
||||
event_meta_properties
|
||||
taxrules
|
||||
categories
|
||||
items
|
||||
|
||||
@@ -65,6 +65,7 @@ valid_date_max date Maximum value f
|
||||
valid_datetime_min datetime Minimum value for date and time questions (optional)
|
||||
valid_datetime_max datetime Maximum value for date and time questions (optional)
|
||||
valid_file_portrait boolean Turn on file validation for portrait photos
|
||||
valid_string_length_min integer Minimum length for string questions (optional)
|
||||
valid_string_length_max integer Maximum length for string questions (optional)
|
||||
dependency_question integer Internal ID of a different question. The current
|
||||
question will only be shown if the question given in
|
||||
@@ -130,6 +131,7 @@ Endpoints
|
||||
"valid_date_max": null,
|
||||
"valid_datetime_min": null,
|
||||
"valid_datetime_max": null,
|
||||
"valid_string_length_min": null,
|
||||
"valid_string_length_max": null,
|
||||
"valid_file_portrait": false,
|
||||
"dependency_question": null,
|
||||
@@ -211,6 +213,7 @@ Endpoints
|
||||
"valid_datetime_min": null,
|
||||
"valid_datetime_max": null,
|
||||
"valid_file_portrait": false,
|
||||
"valid_string_length_min": null,
|
||||
"valid_string_length_max": null,
|
||||
"dependency_question": null,
|
||||
"dependency_value": null,
|
||||
@@ -315,6 +318,7 @@ Endpoints
|
||||
"valid_datetime_min": null,
|
||||
"valid_datetime_max": null,
|
||||
"valid_file_portrait": false,
|
||||
"valid_string_length_min": null,
|
||||
"valid_string_length_max": null,
|
||||
"options": [
|
||||
{
|
||||
@@ -399,6 +403,7 @@ Endpoints
|
||||
"valid_datetime_min": null,
|
||||
"valid_datetime_max": null,
|
||||
"valid_file_portrait": false,
|
||||
"valid_string_length_min": null,
|
||||
"valid_string_length_max": null,
|
||||
"options": [
|
||||
{
|
||||
|
||||
@@ -116,6 +116,7 @@ Endpoints
|
||||
|
||||
:query integer page: The page number in case of a multi-page result set, default is 1
|
||||
:query string code: Only show the voucher with the given voucher code.
|
||||
:query string search: Only show the voucher with the given query found in the code, tag, or comment.
|
||||
:query integer max_usages: Only show vouchers with the given maximal number of usages.
|
||||
:query integer redeemed: Only show vouchers with the given number of redemptions. Note that this doesn't tell you if
|
||||
the voucher can still be redeemed, as this also depends on ``max_usages``. See the
|
||||
|
||||
+57
-1
@@ -8,7 +8,63 @@ import vuePug from 'eslint-plugin-vue-pug'
|
||||
|
||||
const ignores = globalIgnores([
|
||||
'**/node_modules',
|
||||
'**/dist'
|
||||
'**/dist',
|
||||
// Vendored code
|
||||
'src/pretix/static/leaflet',
|
||||
'src/pretix/static/clipboard',
|
||||
'src/pretix/static/cropper',
|
||||
'src/pretix/static/lightbox',
|
||||
'src/pretix/static/are-you-sure',
|
||||
'src/pretix/static/vuejs',
|
||||
'src/pretix/static/fontawesome',
|
||||
'src/pretix/static/typeahead',
|
||||
'src/pretix/static/moment',
|
||||
'src/pretix/static/pdfjs',
|
||||
'src/pretix/static/sortable',
|
||||
'src/pretix/static/iframeresizer',
|
||||
'src/pretix/static/bootstrap',
|
||||
'src/pretix/static/d3',
|
||||
'src/pretix/static/jsi18n',
|
||||
'src/pretix/static/fabric',
|
||||
'src/pretix/static/datetimepicker',
|
||||
'src/pretix/static/charts',
|
||||
'src/pretix/static/fileupload',
|
||||
'src/pretix/static/seating',
|
||||
'src/pretix/static/rest_framework',
|
||||
'src/pretix/static/select2',
|
||||
'src/pretix/static/schema',
|
||||
'src/pretix/static/slider',
|
||||
'src/pretix/static/jquery',
|
||||
'src/pretix/static/colorpicker',
|
||||
'src/pretix/static/rrule',
|
||||
'src/pretix/static/pretixcontrol/js/jquery.qrcode.min.js',
|
||||
'src/pretix/static/pretixpresale/js/widget/docready.js',
|
||||
// Pre-vue JS code
|
||||
'src/pretix/static/pretixbase/js/addressform.js',
|
||||
'src/pretix/static/pretixbase/js/asynctask.js',
|
||||
'src/pretix/static/pretixbase/js/details.js',
|
||||
'src/pretix/static/pretixbase/js/gettextstub.js',
|
||||
'src/pretix/static/pretixbase/js/i18nstring.js',
|
||||
'src/pretix/static/pretixcontrol/js/menu.js',
|
||||
'src/pretix/static/pretixcontrol/js/ui/editor.js',
|
||||
'src/pretix/static/pretixcontrol/js/ui/geo.js',
|
||||
'src/pretix/static/pretixcontrol/js/ui/main.js',
|
||||
'src/pretix/static/pretixcontrol/js/ui/subevent.js',
|
||||
'src/pretix/static/pretixcontrol/js/ui/variations.js',
|
||||
'src/pretix/static/pretixcontrol/js/ui/webauthn.js',
|
||||
'src/pretix/static/pretixpresale/js/ui/cart.js',
|
||||
'src/pretix/static/pretixpresale/js/ui/main.js',
|
||||
'src/pretix/static/pretixpresale/js/ui/questions.js',
|
||||
'src/pretix/static/pretixpresale/js/widget/floatformat.js',
|
||||
'src/pretix/static/pretixpresale/js/widget/widget.js',
|
||||
'src/pretix/plugins/banktransfer/static',
|
||||
'src/pretix/plugins/paypal2/static',
|
||||
'src/pretix/plugins/statistics/static',
|
||||
'src/pretix/plugins/stripe/static',
|
||||
// Plugin checkouts
|
||||
'local',
|
||||
// docs
|
||||
'doc',
|
||||
])
|
||||
|
||||
export default defineConfig([
|
||||
|
||||
Generated
+42
-28
@@ -194,16 +194,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/config-array/node_modules/brace-expansion": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
|
||||
"integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
|
||||
"version": "5.0.12",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz",
|
||||
"integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/config-array/node_modules/minimatch": {
|
||||
@@ -294,29 +294,43 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@humanfs/core": {
|
||||
"version": "0.19.1",
|
||||
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
|
||||
"integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
|
||||
"integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@humanfs/types": "^0.15.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanfs/node": {
|
||||
"version": "0.16.7",
|
||||
"resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
|
||||
"integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
|
||||
"version": "0.16.8",
|
||||
"resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz",
|
||||
"integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@humanfs/core": "^0.19.1",
|
||||
"@humanfs/core": "^0.19.2",
|
||||
"@humanfs/types": "^0.15.0",
|
||||
"@humanwhocodes/retry": "^0.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanfs/types": {
|
||||
"version": "0.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz",
|
||||
"integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=18.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/module-importer": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
|
||||
@@ -1325,16 +1339,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
|
||||
"integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
|
||||
"version": "5.0.12",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz",
|
||||
"integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
|
||||
@@ -1683,9 +1697,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||
"version": "2.1.7",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz",
|
||||
"integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2108,16 +2122,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/brace-expansion": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
|
||||
"integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
|
||||
"version": "5.0.12",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz",
|
||||
"integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/eslint-visitor-keys": {
|
||||
@@ -3380,9 +3394,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-selector-parser": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz",
|
||||
"integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==",
|
||||
"version": "7.1.5",
|
||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.5.tgz",
|
||||
"integrity": "sha512-KvvtD7SrlBP7dlgkBghEE3r84CABm5SmV2aNcG4oCA+qDnJ/tvKonFVvwWAyyWUEwxuNawdfEAZKP9zM3oZ2Uw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -4148,9 +4162,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/smol-toml": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.1.tgz",
|
||||
"integrity": "sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==",
|
||||
"version": "1.7.1",
|
||||
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.7.1.tgz",
|
||||
"integrity": "sha512-PPlsspAZ4jbMBu5DMFhfUGDQLu/vrL4SyBROVS37x8ynnVmFIs1VPBz1Co8Xks3TvpIaZXmU85y4DrQ+UyVFoQ==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
|
||||
+13
-12
@@ -33,14 +33,14 @@ dependencies = [
|
||||
"bleach==6.4.*",
|
||||
"celery==5.6.*",
|
||||
"chardet==5.2.*",
|
||||
"cryptography>=50.0.0",
|
||||
"cryptography>=50.0.1",
|
||||
"css-inline==0.21.*",
|
||||
"defusedcsv>=3.0.0",
|
||||
"dnspython==2.*",
|
||||
"Django[argon2]==5.2.*",
|
||||
"Django[argon2]==5.2.*,>=5.2.17",
|
||||
"django-bootstrap3==26.2",
|
||||
"django-compressor==4.6.0",
|
||||
"django-countries==9.0.*",
|
||||
"django-countries==9.1.*",
|
||||
"django-filter==26.1",
|
||||
"django-formset-js-improved==0.5.0.5",
|
||||
"django-formtools==2.7",
|
||||
@@ -56,7 +56,7 @@ dependencies = [
|
||||
"django-querytagger==0.0.3",
|
||||
"django-redis==7.0.*",
|
||||
"django-scopes==2.1.*",
|
||||
"django-statici18n==2.7.*",
|
||||
"django-statici18n==2.8.*",
|
||||
"djangorestframework==3.17.*",
|
||||
"dnspython==2.8.*",
|
||||
"drf_ujson2==1.7.*",
|
||||
@@ -67,7 +67,7 @@ dependencies = [
|
||||
"kombu==5.6.*",
|
||||
"libsass==0.23.*",
|
||||
"lxml",
|
||||
"markdown==3.10.3", # 3.3.5 requires importlib-metadata>=4.4, but django-bootstrap3 requires importlib-metadata<3.
|
||||
"markdown==3.11", # 3.3.5 requires importlib-metadata>=4.4, but django-bootstrap3 requires importlib-metadata<3.
|
||||
# We can upgrade markdown again once django-bootstrap3 upgrades or once we drop Python 3.6 and 3.7
|
||||
"mt-940==4.30.*",
|
||||
"oauthlib==3.3.*",
|
||||
@@ -75,16 +75,16 @@ dependencies = [
|
||||
"packaging",
|
||||
"paypalrestsdk==1.13.*",
|
||||
"paypal-checkout-serversdk==1.0.*",
|
||||
"PyJWT==2.13.*",
|
||||
"PyJWT==2.15.*",
|
||||
"phonenumberslite==9.0.*",
|
||||
"Pillow==12.3.*",
|
||||
"pretix-plugin-build",
|
||||
"protobuf==7.35.*",
|
||||
"protobuf==7.36.*",
|
||||
"psycopg2-binary",
|
||||
"pycountry",
|
||||
"pycparser==3.0",
|
||||
"pycryptodome==3.23.*",
|
||||
"pypdf==6.5.*",
|
||||
"pypdf==6.19.*",
|
||||
"python-bidi==0.6.*", # Support for Arabic in reportlab
|
||||
"python-dateutil==2.9.*",
|
||||
"pytz",
|
||||
@@ -94,7 +94,7 @@ dependencies = [
|
||||
"redis==7.4.*",
|
||||
"reportlab==5.0.*",
|
||||
"requests==2.34.*",
|
||||
"sentry-sdk==2.66.*",
|
||||
"sentry-sdk==2.70.*",
|
||||
"sepaxml==2.7.*",
|
||||
"stripe==7.9.*",
|
||||
"text-unidecode==1.*",
|
||||
@@ -112,10 +112,10 @@ dev = [
|
||||
"aiohttp==3.14.*",
|
||||
"coverage",
|
||||
"coveralls",
|
||||
"fakeredis==2.37.*",
|
||||
"flake8==7.3.*",
|
||||
"fakeredis==2.38.*",
|
||||
"flake8==7.4.*",
|
||||
"freezegun",
|
||||
"isort==8.0.*",
|
||||
"isort==9.0.*",
|
||||
"pep8-naming==0.15.*",
|
||||
"potypo",
|
||||
"pytest-asyncio>=1.4.0",
|
||||
@@ -129,6 +129,7 @@ dev = [
|
||||
"pytest==9.1.*",
|
||||
"playwright",
|
||||
"responses",
|
||||
"pypdfium2"
|
||||
]
|
||||
|
||||
[project.entry-points."distutils.commands"]
|
||||
|
||||
@@ -26,7 +26,6 @@ ignore =
|
||||
src/tests/plugins/*
|
||||
src/tests/plugins/badges/*
|
||||
src/tests/plugins/banktransfer/*
|
||||
src/tests/plugins/paypal/*
|
||||
src/tests/plugins/paypal2/*
|
||||
src/tests/plugins/pretixdroid/*
|
||||
src/tests/plugins/stripe/*
|
||||
|
||||
@@ -30,3 +30,5 @@ npminstall:
|
||||
npmbuild:
|
||||
npm run build
|
||||
|
||||
licenseheaders:
|
||||
licenseheaders -t ../.licenseheader -E .py -x "*/migrations/*.py"
|
||||
@@ -19,4 +19,4 @@
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
__version__ = "2026.8.0.dev0"
|
||||
__version__ = "2026.9.0.dev0"
|
||||
|
||||
@@ -19,13 +19,12 @@
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
from django.dispatch import receiver
|
||||
|
||||
from pretix.base.signals import register_payment_providers
|
||||
def get_session_key_for_api_auth(user, auth):
|
||||
if user.is_authenticated:
|
||||
return f'api-upload-User-{user.pk}'
|
||||
else:
|
||||
return f'api-upload-{str(type(auth))}-{auth.pk}'
|
||||
|
||||
|
||||
@receiver(register_payment_providers, dispatch_uid="payment_paypal")
|
||||
def register_payment_provider(sender, **kwargs):
|
||||
from .payment import Paypal
|
||||
return Paypal
|
||||
def get_session_key_for_api_request(request):
|
||||
return get_session_key_for_api_auth(request.user, request.auth)
|
||||
@@ -101,6 +101,10 @@ class OAuthAccessToken(AbstractAccessToken):
|
||||
self.expires = now() - timedelta(hours=1)
|
||||
self.save(update_fields=['expires'])
|
||||
|
||||
def is_valid(self, scopes=None):
|
||||
# Can maybe be removed after upgrading django-oauth-toolkit to 3.4.1
|
||||
return super().is_valid(scopes) and self.application.is_usable(None)
|
||||
|
||||
|
||||
class OAuthRefreshToken(AbstractRefreshToken):
|
||||
application = models.ForeignKey(
|
||||
|
||||
@@ -702,8 +702,12 @@ class TaxRuleSerializer(CountryFieldMixin, I18nAwareModelSerializer):
|
||||
return super().save(**kwargs)
|
||||
|
||||
def validate_default(self, value):
|
||||
if not value and self.instance.default:
|
||||
raise ValidationError("You can't remove the default property, instead set it on another tax rule.")
|
||||
if not value:
|
||||
if self.instance:
|
||||
if self.instance.default:
|
||||
raise ValidationError("You can't remove the default property, instead set it on another tax rule.")
|
||||
elif not self.context["event"].tax_rules.exists():
|
||||
raise ValidationError("You can't remove the default property as there is only one tax rule.")
|
||||
return value
|
||||
|
||||
|
||||
|
||||
@@ -37,6 +37,8 @@ from collections import OrderedDict
|
||||
from django.core.exceptions import ValidationError
|
||||
from rest_framework import serializers
|
||||
|
||||
from pretix.api.auth.utils import get_session_key_for_api_request
|
||||
|
||||
|
||||
def remove_duplicates_from_list(data):
|
||||
return list(OrderedDict.fromkeys(data))
|
||||
@@ -83,10 +85,16 @@ class UploadedFileField(serializers.Field):
|
||||
request = self.context.get('request', None)
|
||||
try:
|
||||
cf = CachedFile.objects.get(
|
||||
session_key=f'api-upload-{str(type(request.user or request.auth))}-{(request.user or request.auth).pk}',
|
||||
file__isnull=False,
|
||||
pk=data[len("file:"):],
|
||||
)
|
||||
if cf.session_key == "api-upload-<class 'django.contrib.auth.models.AnonymousUser'>-None":
|
||||
# OK, backwards-compatibility of a security bug fixed 2026-09, delete this at some point, but should
|
||||
# also be harmless because all files with this key are expired one day after deployment of this fix
|
||||
# and no new files with this key are created
|
||||
pass
|
||||
elif cf.session_key != get_session_key_for_api_request(request):
|
||||
self.fail('not_found')
|
||||
except (ValidationError, IndexError): # invalid uuid
|
||||
self.fail('not_found')
|
||||
except CachedFile.DoesNotExist:
|
||||
|
||||
@@ -550,7 +550,7 @@ class QuestionSerializer(I18nAwareModelSerializer):
|
||||
'ask_during_checkin', 'show_during_checkin', 'identifier', 'dependency_question', 'dependency_values',
|
||||
'hidden', 'dependency_value', 'print_on_invoice', 'help_text', 'valid_number_min',
|
||||
'valid_number_max', 'valid_date_min', 'valid_date_max', 'valid_datetime_min', 'valid_datetime_max',
|
||||
'valid_string_length_max', 'valid_file_portrait')
|
||||
'valid_string_length_max', 'valid_string_length_min', 'valid_file_portrait')
|
||||
|
||||
def validate_identifier(self, value):
|
||||
Question._clean_identifier(self.context['event'], value, self.instance)
|
||||
|
||||
@@ -41,6 +41,7 @@ from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.relations import SlugRelatedField
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
from pretix.api.auth.utils import get_session_key_for_api_request
|
||||
from pretix.api.serializers import CompatDecimalField, CompatibleJSONField
|
||||
from pretix.api.serializers.event import SubEventSerializer
|
||||
from pretix.api.serializers.forms import form_field_to_serializer_field
|
||||
@@ -258,16 +259,21 @@ class AnswerSerializer(I18nAwareModelSerializer):
|
||||
if data['answer'] == 'file:keep':
|
||||
return data
|
||||
try:
|
||||
ao = self.context["request"].user or self.context["request"].auth
|
||||
cf = CachedFile.objects.get(
|
||||
session_key=f'api-upload-{str(type(ao))}-{ao.pk}',
|
||||
file__isnull=False,
|
||||
pk=data['answer'][len("file:"):],
|
||||
)
|
||||
if cf.session_key == "api-upload-<class 'django.contrib.auth.models.AnonymousUser'>-None":
|
||||
# OK, backwards-compatibility of a security bug fixed 2026-09, delete this at some point, but should
|
||||
# also be harmless because all files with this key are expired one day after deployment of this fix
|
||||
# and no new files with this key are created
|
||||
pass
|
||||
elif cf.session_key != get_session_key_for_api_request(self.context["request"]):
|
||||
raise ValidationError('The submitted file ID "{fid}" was not found.'.format(fid=data['answer']))
|
||||
except (ValidationError, IndexError): # invalid uuid
|
||||
raise ValidationError('The submitted file ID "{fid}" was not found.'.format(fid=data))
|
||||
raise ValidationError('The submitted file ID "{fid}" was not found.'.format(fid=data['answer']))
|
||||
except CachedFile.DoesNotExist:
|
||||
raise ValidationError('The submitted file ID "{fid}" was not found.'.format(fid=data))
|
||||
raise ValidationError('The submitted file ID "{fid}" was not found.'.format(fid=data['answer']))
|
||||
|
||||
allowed_types = (
|
||||
'image/png', 'image/jpeg', 'image/gif', 'application/pdf'
|
||||
|
||||
@@ -28,6 +28,7 @@ from django.db import transaction
|
||||
from django.db.models import Q
|
||||
from django.utils.crypto import get_random_string
|
||||
from django.utils.translation import gettext, gettext_lazy as _
|
||||
from i18nfield.rest_framework import I18nField
|
||||
from rest_framework import serializers
|
||||
from rest_framework.exceptions import ValidationError
|
||||
|
||||
@@ -40,9 +41,10 @@ from pretix.api.serializers.settings import SettingsSerializer
|
||||
from pretix.base.auth import get_auth_backends
|
||||
from pretix.base.i18n import get_language_without_region
|
||||
from pretix.base.models import (
|
||||
Customer, Device, GiftCard, GiftCardAcceptance, GiftCardTransaction,
|
||||
Membership, MembershipType, OrderPosition, Organizer, ReusableMedium,
|
||||
SalesChannel, SeatingPlan, Team, TeamAPIToken, TeamInvite, User,
|
||||
Customer, Device, EventMetaProperty, GiftCard, GiftCardAcceptance,
|
||||
GiftCardTransaction, Membership, MembershipType, OrderPosition, Organizer,
|
||||
ReusableMedium, SalesChannel, SeatingPlan, Team, TeamAPIToken, TeamInvite,
|
||||
User,
|
||||
)
|
||||
from pretix.base.models.seating import SeatingPlanLayoutValidator
|
||||
from pretix.base.permissions import (
|
||||
@@ -426,6 +428,8 @@ class TeamSerializer(serializers.ModelSerializer):
|
||||
for k, v in OLD_TO_NEW_ORGANIZER_MIGRATION.items():
|
||||
if full_data.get(k) is True:
|
||||
data["limit_organizer_permissions"].update({kk: True for kk in v})
|
||||
for key in list(k for k in data if k.startswith("can_")):
|
||||
del data[key]
|
||||
|
||||
if full_data.get('limit_events') and full_data.get('all_events'):
|
||||
raise ValidationError('Do not set both limit_events and all_events.')
|
||||
@@ -638,3 +642,88 @@ class OrganizerSettingsSerializer(SettingsSerializer):
|
||||
)
|
||||
# TODO: make sure pub is always correct
|
||||
return 'pub/' + fname
|
||||
|
||||
|
||||
class MetaPropertyListField(serializers.ListField):
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs["validators"] = kwargs.pop("validators", [])
|
||||
|
||||
def validate_keys_unique(choices):
|
||||
if not choices:
|
||||
return
|
||||
keys = [c.get("key") for c in choices]
|
||||
if len(set(keys)) < len(keys):
|
||||
raise ValidationError("The key for each meta property value option must be unique.")
|
||||
|
||||
kwargs["validators"].append(
|
||||
validate_keys_unique
|
||||
)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class MetaPropertyDictField(serializers.DictField):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.label_child = kwargs.pop("label_child", I18nField())
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def to_representation(self, value):
|
||||
# django added unneccessary keys DELETE, ORDER through formsets, filter them here for backwards compat
|
||||
d = {
|
||||
"key": value["key"]
|
||||
}
|
||||
if "label" in value:
|
||||
d["label"] = self.label_child.to_representation(value["label"])
|
||||
|
||||
return super().to_representation(d)
|
||||
|
||||
def to_internal_value(self, data):
|
||||
if not isinstance(data, dict):
|
||||
raise ValidationError("Meta property value options must be a dict.")
|
||||
|
||||
if not isinstance(data.get("key"), str):
|
||||
raise ValidationError("Meta property value options must have a key of type string.")
|
||||
|
||||
if any(k not in {"key", "label"} for k in data.keys()):
|
||||
raise ValidationError("Meta property value options may only have a key and optionally a label.")
|
||||
|
||||
if "label" in data:
|
||||
try:
|
||||
data["label"] = self.label_child.to_internal_value(data["label"])
|
||||
except ValidationError as e:
|
||||
raise ValidationError({"label": e.detail})
|
||||
|
||||
return super().to_internal_value(data)
|
||||
|
||||
|
||||
class EventMetaPropertiesSerializer(I18nAwareModelSerializer):
|
||||
choices = MetaPropertyListField(
|
||||
child=MetaPropertyDictField(
|
||||
label_child=I18nField()
|
||||
),
|
||||
allow_null=True,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = EventMetaProperty
|
||||
fields = (
|
||||
'id', 'name', 'default', 'required', 'protected', 'filter_public', 'public_label', 'filter_allowed',
|
||||
'choices'
|
||||
)
|
||||
|
||||
def validate(self, data):
|
||||
data = super().validate(data)
|
||||
full_data = self.to_internal_value(self.to_representation(self.instance)) if self.instance else {}
|
||||
full_data.update(data)
|
||||
|
||||
choices = full_data.get("choices")
|
||||
default = full_data.get("default")
|
||||
if choices and default:
|
||||
choice_keys = [c.get("key") for c in choices]
|
||||
if default not in choice_keys:
|
||||
raise ValidationError("You cannot set a default value that is not a valid value.")
|
||||
|
||||
if not choices and "choices" in data:
|
||||
# normalize empty dict to None
|
||||
data["choices"] = None
|
||||
return data
|
||||
|
||||
@@ -68,6 +68,7 @@ orga_router.register(r'scheduled_exports', exporters.ScheduledOrganizerExportVie
|
||||
orga_router.register(r'exporters', exporters.OrganizerExportersViewSet, basename='exporters')
|
||||
orga_router.register(r'transactions', order.OrganizerTransactionViewSet)
|
||||
orga_router.register(r'orderpositions', order.OrganizerOrderPositionViewSet, basename='orderpositions')
|
||||
orga_router.register(r'event_meta_properties', organizer.EventMetaPropertiesViewSet)
|
||||
|
||||
team_router = routers.DefaultRouter()
|
||||
team_router.register(r'members', organizer.TeamMemberViewSet)
|
||||
|
||||
@@ -50,6 +50,7 @@ from rest_framework.generics import ListAPIView
|
||||
from rest_framework.permissions import SAFE_METHODS
|
||||
from rest_framework.response import Response
|
||||
|
||||
from pretix.api.auth.utils import get_session_key_for_api_auth
|
||||
from pretix.api.serializers.checkin import (
|
||||
CheckinListSerializer, CheckinRPCAnnulInputSerializer,
|
||||
CheckinRPCRedeemInputSerializer, MiniCheckinListSerializer,
|
||||
@@ -331,10 +332,16 @@ with scopes_disabled():
|
||||
def _handle_file_upload(data, user, auth):
|
||||
try:
|
||||
cf = CachedFile.objects.get(
|
||||
session_key=f'api-upload-{str(type(user or auth))}-{(user or auth).pk}',
|
||||
file__isnull=False,
|
||||
pk=data[len("file:"):],
|
||||
)
|
||||
if cf.session_key == "api-upload-<class 'django.contrib.auth.models.AnonymousUser'>-None":
|
||||
# OK, backwards-compatibility of a security bug fixed 2026-09, delete this at some point, but should
|
||||
# also be harmless because all files with this key are expired one day after deployment of this fix
|
||||
# and no new files with this key are created
|
||||
pass
|
||||
elif cf.session_key != get_session_key_for_api_auth(user, auth):
|
||||
raise ValidationError('The submitted file ID "{fid}" was not found.'.format(fid=data))
|
||||
except (ValidationError, BaseValidationError, IndexError): # invalid uuid
|
||||
raise ValidationError('The submitted file ID "{fid}" was not found.'.format(fid=data))
|
||||
except CachedFile.DoesNotExist:
|
||||
|
||||
@@ -1683,7 +1683,7 @@ class PaymentViewSet(CreateModelMixin, viewsets.ReadOnlyModelViewSet):
|
||||
@action(detail=True, methods=['POST'])
|
||||
def confirm(self, request, **kwargs):
|
||||
payment = self.get_object()
|
||||
force = request.data.get('force', False)
|
||||
force = request.data.get('force', False) if request.data else False
|
||||
send_mail = request.data.get('send_email', True) if request.data else True
|
||||
|
||||
if payment.state not in (OrderPayment.PAYMENT_STATE_PENDING, OrderPayment.PAYMENT_STATE_CREATED):
|
||||
|
||||
@@ -44,15 +44,16 @@ from pretix.api.models import OAuthAccessToken
|
||||
from pretix.api.pagination import TotalOrderingFilter
|
||||
from pretix.api.serializers.organizer import (
|
||||
CustomerCreateSerializer, CustomerSerializer, DeviceSerializer,
|
||||
GiftCardSerializer, GiftCardTransactionSerializer, MembershipSerializer,
|
||||
EventMetaPropertiesSerializer, GiftCardSerializer,
|
||||
GiftCardTransactionSerializer, MembershipSerializer,
|
||||
MembershipTypeSerializer, OrganizerSerializer, OrganizerSettingsSerializer,
|
||||
SalesChannelSerializer, SeatingPlanSerializer, TeamAPITokenSerializer,
|
||||
TeamInviteSerializer, TeamMemberSerializer, TeamSerializer,
|
||||
)
|
||||
from pretix.base.models import (
|
||||
Customer, Device, Event, GiftCard, GiftCardTransaction, LogEntry,
|
||||
Membership, MembershipType, Organizer, SalesChannel, SeatingPlan, Team,
|
||||
TeamAPIToken, TeamInvite, User,
|
||||
Customer, Device, Event, EventMetaProperty, GiftCard, GiftCardTransaction,
|
||||
LogEntry, Membership, MembershipType, Organizer, SalesChannel, SeatingPlan,
|
||||
Team, TeamAPIToken, TeamInvite, User,
|
||||
)
|
||||
from pretix.base.plugins import (
|
||||
PLUGIN_LEVEL_EVENT, PLUGIN_LEVEL_EVENT_ORGANIZER_HYBRID,
|
||||
@@ -846,3 +847,49 @@ class SalesChannelViewSet(viewsets.ModelViewSet):
|
||||
data={'id': instance.pk}
|
||||
)
|
||||
instance.delete()
|
||||
|
||||
|
||||
class EventMetaPropertiesViewSet(viewsets.ModelViewSet):
|
||||
serializer_class = EventMetaPropertiesSerializer
|
||||
queryset = EventMetaProperty.objects.none()
|
||||
write_permission = 'organizer.settings.general:write'
|
||||
|
||||
def get_queryset(self):
|
||||
return self.request.organizer.meta_properties.all()
|
||||
|
||||
def get_serializer_context(self):
|
||||
ctx = super().get_serializer_context()
|
||||
ctx['organizer'] = self.request.organizer
|
||||
return ctx
|
||||
|
||||
@transaction.atomic()
|
||||
def perform_destroy(self, instance):
|
||||
instance.log_action(
|
||||
'pretix.property.deleted',
|
||||
user=self.request.user,
|
||||
auth=self.request.auth,
|
||||
data={'id': instance.pk}
|
||||
)
|
||||
instance.delete()
|
||||
|
||||
@transaction.atomic()
|
||||
def perform_create(self, serializer):
|
||||
inst = serializer.save(organizer_id=self.request.organizer.pk)
|
||||
serializer.instance.log_action(
|
||||
'pretix.property.created',
|
||||
user=self.request.user,
|
||||
auth=self.request.auth,
|
||||
data=self.request.data,
|
||||
)
|
||||
return inst
|
||||
|
||||
@transaction.atomic()
|
||||
def perform_update(self, serializer):
|
||||
inst = serializer.save(organizer_id=self.request.organizer.pk)
|
||||
serializer.instance.log_action(
|
||||
'pretix.property.changed',
|
||||
user=self.request.user,
|
||||
auth=self.request.auth,
|
||||
data=self.request.data,
|
||||
)
|
||||
return inst
|
||||
|
||||
@@ -33,6 +33,7 @@ from rest_framework.views import APIView
|
||||
from pretix.api.auth.device import DeviceTokenAuthentication
|
||||
from pretix.api.auth.permission import AnyAuthenticatedClientPermission
|
||||
from pretix.api.auth.token import TeamTokenAuthentication
|
||||
from pretix.api.auth.utils import get_session_key_for_api_request
|
||||
from pretix.base.models import CachedFile
|
||||
from pretix.helpers.images import (
|
||||
IMAGE_TYPES, validate_uploaded_file_for_valid_image,
|
||||
@@ -78,7 +79,7 @@ class UploadView(APIView):
|
||||
web_download=False,
|
||||
filename=file_obj.name,
|
||||
type=content_type,
|
||||
session_key=f'api-upload-{str(type(request.user or request.auth))}-{(request.user or request.auth).pk}'
|
||||
session_key=get_session_key_for_api_request(request)
|
||||
)
|
||||
cf.file.save(file_obj.name, file_obj)
|
||||
cf.save()
|
||||
|
||||
@@ -40,6 +40,7 @@ with scopes_disabled():
|
||||
class VoucherFilter(FilterSet):
|
||||
active = BooleanFilter(method='filter_active')
|
||||
code = CharFilter(lookup_expr='iexact')
|
||||
search = CharFilter(method='search_qs')
|
||||
|
||||
class Meta:
|
||||
model = Voucher
|
||||
@@ -54,6 +55,9 @@ with scopes_disabled():
|
||||
return queryset.filter(Q(redeemed__gte=F('max_usages')) |
|
||||
(Q(valid_until__isnull=False) & Q(valid_until__lte=now())))
|
||||
|
||||
def search_qs(self, qs, name, value):
|
||||
return qs.filter(Q(code__icontains=value) | Q(tag__icontains=value) | Q(comment__icontains=value))
|
||||
|
||||
|
||||
class VoucherViewSet(viewsets.ModelViewSet):
|
||||
serializer_class = VoucherSerializer
|
||||
|
||||
@@ -27,7 +27,7 @@ from datetime import timedelta
|
||||
from functools import cached_property
|
||||
from typing import List, Optional, Protocol
|
||||
|
||||
import sentry_sdk
|
||||
from django.conf import settings
|
||||
from django.db import DatabaseError, transaction
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
@@ -236,7 +236,9 @@ class OutboundSyncProvider:
|
||||
# model changes saved by set_sync_error / clear_in_flight calls below
|
||||
if sq.failed_attempts >= self.max_attempts:
|
||||
logger.exception('Failed to sync order (max attempts exceeded)')
|
||||
sentry_sdk.capture_exception(e)
|
||||
if settings.SENTRY_ENABLED:
|
||||
import sentry_sdk
|
||||
sentry_sdk.capture_exception(e)
|
||||
sq.set_sync_error("exceeded", e.messages, e.full_message)
|
||||
else:
|
||||
logger.info(
|
||||
@@ -247,7 +249,9 @@ class OutboundSyncProvider:
|
||||
sq.clear_in_flight()
|
||||
except Exception as e:
|
||||
logger.exception('Failed to sync order (unhandled exception)')
|
||||
sentry_sdk.capture_exception(e)
|
||||
if settings.SENTRY_ENABLED:
|
||||
import sentry_sdk
|
||||
sentry_sdk.capture_exception(e)
|
||||
sq.set_sync_error("internal", [], str(e))
|
||||
|
||||
@cached_property
|
||||
|
||||
@@ -54,6 +54,7 @@ from ...control.forms.filter import get_all_payment_providers
|
||||
from ...helpers import GroupConcat
|
||||
from ...helpers.iter import chunked_iterable
|
||||
from ..exporter import BaseExporter, MultiSheetListExporter
|
||||
from ..invoicing.transmission import get_transmission_types
|
||||
from ..services.export import ExportError
|
||||
from ..services.invoices import invoice_pdf_task
|
||||
from ..signals import (
|
||||
@@ -197,7 +198,7 @@ class InvoiceDataExporter(InvoiceExporterMixin, MultiSheetListExporter):
|
||||
def iterate_sheet(self, form_data, sheet):
|
||||
_ = gettext
|
||||
if sheet == 'invoices':
|
||||
yield [
|
||||
headers = [
|
||||
_('Invoice number'),
|
||||
_('Date'),
|
||||
_('Order code'),
|
||||
@@ -230,8 +231,18 @@ class InvoiceDataExporter(InvoiceExporterMixin, MultiSheetListExporter):
|
||||
_('Total value (without taxes)'),
|
||||
_('Payment matching IDs'),
|
||||
_('Payment providers'),
|
||||
_('Transmission type'),
|
||||
_('Transmission status'),
|
||||
_('Transmission date'),
|
||||
]
|
||||
|
||||
transmission_types = get_transmission_types()
|
||||
for tt in transmission_types:
|
||||
for c in tt.describe_info_columns():
|
||||
headers.append(str(tt.verbose_name) + ': ' + str(c))
|
||||
|
||||
yield headers
|
||||
|
||||
p_providers = OrderPayment.objects.filter(
|
||||
order=OuterRef('order'),
|
||||
state__in=(OrderPayment.PAYMENT_STATE_CONFIRMED, OrderPayment.PAYMENT_STATE_REFUNDED,
|
||||
@@ -242,7 +253,7 @@ class InvoiceDataExporter(InvoiceExporterMixin, MultiSheetListExporter):
|
||||
'm'
|
||||
).order_by()
|
||||
|
||||
base_qs = self.invoices_queryset(form_data)\
|
||||
base_qs = self.invoices_queryset(form_data)
|
||||
|
||||
qs = base_qs.select_related(
|
||||
'order', 'refers'
|
||||
@@ -280,7 +291,7 @@ class InvoiceDataExporter(InvoiceExporterMixin, MultiSheetListExporter):
|
||||
if mid:
|
||||
pmis.append(mid)
|
||||
pmi = '\n'.join(pmis)
|
||||
yield [
|
||||
line = [
|
||||
i.full_invoice_no,
|
||||
date_format(i.date, "SHORT_DATE_FORMAT"),
|
||||
i.order.code,
|
||||
@@ -315,8 +326,20 @@ class InvoiceDataExporter(InvoiceExporterMixin, MultiSheetListExporter):
|
||||
', '.join([
|
||||
str(self.providers.get(p, p)) for p in sorted(set((i.payment_providers or '').split(',')))
|
||||
if p and p != 'free'
|
||||
])
|
||||
]),
|
||||
i.transmission_type_instance.verbose_name,
|
||||
i.get_transmission_status_display(),
|
||||
date_format(i.transmission_date, "SHORT_DATETIME_FORMAT") if i.transmission_date else "",
|
||||
]
|
||||
for tt in transmission_types:
|
||||
if tt.identifier == i.transmission_type:
|
||||
described = dict(tt.describe_info(i.invoice_to_transmission_info, i.invoice_to_country, i.invoice_to_is_business))
|
||||
for c in tt.describe_info_columns():
|
||||
line.append(described.get(c, ""))
|
||||
else:
|
||||
for c in tt.describe_info_columns():
|
||||
line.append("")
|
||||
yield line
|
||||
elif sheet == 'lines':
|
||||
yield [
|
||||
_('Invoice number'),
|
||||
|
||||
@@ -350,16 +350,22 @@ class WrappedPhonePrefixSelect(Select):
|
||||
return super().render(name, value or self.initial, *args, **kwargs)
|
||||
|
||||
def get_context(self, name, value, attrs):
|
||||
if value and self.choices[1][0] != value:
|
||||
matching_choices = len([1 for p, c in self.choices if p == value])
|
||||
# self.choices is lazy evaluated, needs to be realized to be modifiable
|
||||
choices = list(self.choices)
|
||||
if value and choices[1][0] != value:
|
||||
matching_choices = len([1 for p, c in choices if p == value])
|
||||
if matching_choices > 1:
|
||||
# Some countries share a phone prefix, for example +1 is used all over the Americas.
|
||||
# This causes a UX problem: If the default value or the existing data is +12125552368,
|
||||
# the widget will just show the first <option> entry with value="+1" as selected,
|
||||
# which alphabetically is America Samoa, although most numbers statistically are from
|
||||
# the US. As a workaround, we detect this case and add an aditional choice value with
|
||||
# the US. As a workaround, we detect this case and add an additional choice value with
|
||||
# just <option value="+1">+1</option> without an explicit country.
|
||||
self.choices.insert(1, (value, value))
|
||||
self.choices = [
|
||||
choices[0],
|
||||
(value, value),
|
||||
*choices[1:],
|
||||
]
|
||||
context = super().get_context(name, value, attrs)
|
||||
return context
|
||||
|
||||
@@ -594,12 +600,16 @@ class PortraitImageField(SizeValidationMixin, ExtValidationMixin, forms.FileFiel
|
||||
image = ImageOps.exif_transpose(image)
|
||||
|
||||
if f._cropdata:
|
||||
image = image.crop((
|
||||
f._cropdata.get('x', 0),
|
||||
f._cropdata.get('y', 0),
|
||||
f._cropdata.get('x', 0) + f._cropdata.get('width', image.width),
|
||||
f._cropdata.get('y', 0) + f._cropdata.get('height', image.height),
|
||||
))
|
||||
left = int(f._cropdata.get('x', 0))
|
||||
top = int(f._cropdata.get('y', 0))
|
||||
right = left + int(f._cropdata.get('width', image.width))
|
||||
bottom = top + int(f._cropdata.get('height', image.height))
|
||||
if left >= image.width or top >= image.height or right > image.width or bottom > image.height:
|
||||
raise ValidationError(
|
||||
self.error_messages['max_dimension'],
|
||||
code='max_dimension',
|
||||
)
|
||||
image = image.crop((left, top, right, bottom))
|
||||
with BytesIO() as output:
|
||||
# This might use a lot of memory, but temporary files are not a good option since
|
||||
# we don't control the cleanup
|
||||
@@ -708,6 +718,7 @@ class BaseQuestionsForm(forms.Form):
|
||||
elif q.type == Question.TYPE_STRING:
|
||||
field = forms.CharField(
|
||||
label=escape(q.question), required=required,
|
||||
min_length=q.valid_string_length_min,
|
||||
max_length=q.valid_string_length_max,
|
||||
help_text=rich_text(q.help_text),
|
||||
initial=initial.answer if initial else None,
|
||||
@@ -715,6 +726,7 @@ class BaseQuestionsForm(forms.Form):
|
||||
elif q.type == Question.TYPE_TEXT:
|
||||
field = forms.CharField(
|
||||
label=escape(q.question), required=required,
|
||||
min_length=q.valid_string_length_min,
|
||||
max_length=q.valid_string_length_max,
|
||||
help_text=rich_text(q.help_text),
|
||||
widget=forms.Textarea,
|
||||
@@ -897,7 +909,7 @@ class BaseQuestionsForm(forms.Form):
|
||||
field.widget.attrs['data-question-dependency-values'] = escapejson_attr(json.dumps(q.dependency_values))
|
||||
if q.type != 'M':
|
||||
field.widget.attrs['required'] = q.required and not self.all_optional
|
||||
field._required = q.required and not self.all_optional
|
||||
field._required = q.required and not self.all_optional
|
||||
field.required = False
|
||||
return field
|
||||
|
||||
@@ -1194,8 +1206,9 @@ class TicketLevelQuestionsForm(BaseQuestionsForm):
|
||||
return field
|
||||
|
||||
def clean(self):
|
||||
from pretix.base.addressvalidation import \
|
||||
validate_address # local import to prevent impact on startup time
|
||||
from pretix.base.addressvalidation import ( # local import to prevent impact on startup time
|
||||
validate_address,
|
||||
)
|
||||
|
||||
d = super().clean()
|
||||
|
||||
@@ -1436,8 +1449,9 @@ class BaseInvoiceAddressForm(forms.ModelForm):
|
||||
self.fields['transmission_type'].widget.attrs['data-trigger-address-info'] = 'on'
|
||||
|
||||
def clean(self):
|
||||
from pretix.base.addressvalidation import \
|
||||
validate_address # local import to prevent impact on startup time
|
||||
from pretix.base.addressvalidation import ( # local import to prevent impact on startup time
|
||||
validate_address,
|
||||
)
|
||||
|
||||
data = self.cleaned_data
|
||||
|
||||
@@ -1491,11 +1505,12 @@ class BaseInvoiceAddressForm(forms.ModelForm):
|
||||
"vat_id": _("This field is required.")
|
||||
})
|
||||
|
||||
if self.validate_vat_id and self.instance.vat_id_validated and 'vat_id' not in self.changed_data:
|
||||
if self.validate_vat_id and self.instance.vat_id_validated and not any(v in self.changed_data for v in ('is_business', 'vat_id', 'country')):
|
||||
pass # Skip re-validation if it is validated
|
||||
elif self.validate_vat_id and vat_id_applicable:
|
||||
try:
|
||||
normalized_id = validate_vat_id(data.get('vat_id'), str(data.get('country')))
|
||||
requester_id = self.request.event.settings.invoice_address_from_vat_id
|
||||
normalized_id = validate_vat_id(data.get('vat_id'), str(data.get('country')), requester_id)
|
||||
self.instance.vat_id_validated = bool(normalized_id)
|
||||
self.instance.vat_id = data['vat_id'] = normalized_id
|
||||
except VATIDFinalError as e:
|
||||
|
||||
@@ -82,8 +82,8 @@ class UserSettingsForm(forms.ModelForm):
|
||||
class User2FADeviceAddForm(forms.Form):
|
||||
name = forms.CharField(label=_('Device name'), max_length=64)
|
||||
devicetype = forms.ChoiceField(label=_('Device type'), widget=forms.RadioSelect, choices=(
|
||||
('totp', _('Smartphone with the Authenticator application')),
|
||||
('webauthn', _('WebAuthn-compatible hardware token (e.g. Yubikey)')),
|
||||
('otp_totp.totpdevice', _('Smartphone with the Authenticator application')),
|
||||
('pretixbase.webauthndevice', _('WebAuthn-compatible hardware token (e.g. Yubikey)')),
|
||||
))
|
||||
|
||||
|
||||
|
||||
@@ -71,8 +71,8 @@ class EmailTransmissionType(TransmissionType):
|
||||
|
||||
def transmission_info_to_form_data(self, transmission_info: dict) -> dict:
|
||||
return {
|
||||
"transmission_email_other": bool(transmission_info.get("transmission_email_address")),
|
||||
"transmission_email_address": transmission_info.get("transmission_email_address"),
|
||||
"transmission_email_other": bool((transmission_info or {}).get("transmission_email_address")),
|
||||
"transmission_email_address": (transmission_info or {}).get("transmission_email_address"),
|
||||
}
|
||||
|
||||
def form_data_to_transmission_info(self, form_data: dict) -> dict:
|
||||
|
||||
@@ -107,6 +107,9 @@ class TransmissionType:
|
||||
def transmission_info_to_form_data(self, transmission_info: dict) -> dict:
|
||||
return transmission_info
|
||||
|
||||
def describe_info_columns(self):
|
||||
return [f.label for f in self.invoice_address_form_fields.values()]
|
||||
|
||||
def describe_info(self, transmission_info: dict, country: Country, is_business: bool):
|
||||
form_data = self.transmission_info_to_form_data(transmission_info)
|
||||
data = []
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# Generated by Django 5.2.12 on 2026-08-19 11:31
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("pretixbase", "0309_alter_questionanswer_unique_together_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="question",
|
||||
name="valid_string_length_min",
|
||||
field=models.PositiveIntegerField(null=True),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,49 @@
|
||||
# Generated by Django 5.2.17 on 2026-09-21 11:30
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
def fix_unshredded_invoices(apps, _):
|
||||
Invoice = apps.get_model("pretixbase", "Invoice")
|
||||
InvoiceLine = apps.get_model("pretixbase", "InvoiceLine")
|
||||
ignore_fields = (
|
||||
# bool/int fields are not listed and skipped automatically
|
||||
'prefix', 'invoice_no', 'full_invoice_no', 'invoice_from', 'invoice_from_name', 'invoice_from_zipcode',
|
||||
'invoice_from_city', 'invoice_from_state', 'invoice_from_country', 'invoice_from_tax_id',
|
||||
'invoice_from_vat_id', 'locale', 'payment_provider_stamp', 'footer_text', 'foreign_currency_display',
|
||||
'foreign_currency_source', 'transmission_type', 'transmission_provider', 'transmission_status',
|
||||
)
|
||||
|
||||
for i in Invoice.objects.filter(shredded=True):
|
||||
for f in Invoice._meta.fields:
|
||||
if f.name in ignore_fields:
|
||||
continue
|
||||
val = getattr(i, f.name, None)
|
||||
if val and isinstance(val, str):
|
||||
setattr(i, f.name, "█")
|
||||
elif val and isinstance(val, list): # jsonfield
|
||||
setattr(i, f.name, [])
|
||||
elif val and isinstance(val, dict): # jsonfield
|
||||
setattr(i, f.name, {"_shredded": True})
|
||||
i.save()
|
||||
|
||||
InvoiceLine.objects.filter(
|
||||
attendee_name__isnull=False,
|
||||
invoice__shredded=True
|
||||
).update(attendee_name="█")
|
||||
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("pretixbase", "0310_question_valid_string_length_min"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
fix_unshredded_invoices,
|
||||
migrations.RunPython.noop,
|
||||
),
|
||||
]
|
||||
@@ -166,6 +166,7 @@ class Device(LoggedModel):
|
||||
)
|
||||
security_profile = models.CharField(
|
||||
max_length=190,
|
||||
verbose_name=_('Security profile'),
|
||||
default='full',
|
||||
null=True,
|
||||
blank=False
|
||||
|
||||
@@ -50,7 +50,7 @@ from dateutil.tz import datetime_exists
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.validators import (
|
||||
MaxLengthValidator, MinValueValidator, RegexValidator,
|
||||
MaxLengthValidator, MinLengthValidator, MinValueValidator, RegexValidator,
|
||||
)
|
||||
from django.db import models
|
||||
from django.db.models import Q
|
||||
@@ -1050,10 +1050,8 @@ class Item(LoggedModel):
|
||||
|
||||
replace_year = valid_until.year
|
||||
replace_month = valid_until.month + self.validity_dynamic_duration_months
|
||||
|
||||
while replace_month > 12:
|
||||
replace_month -= 12
|
||||
replace_year += 1
|
||||
replace_year += (replace_month - 1) // 12
|
||||
replace_month = ((replace_month - 1) % 12) + 1
|
||||
max_day = calendar.monthrange(replace_year, replace_month)[1]
|
||||
replace_date = date(
|
||||
year=replace_year,
|
||||
@@ -1731,6 +1729,11 @@ class Question(LoggedModel):
|
||||
valid_datetime_max = models.DateTimeField(null=True, blank=True,
|
||||
verbose_name=_('Maximum value'),
|
||||
help_text=_('Currently not supported in our apps and during check-in'))
|
||||
valid_string_length_min = models.PositiveIntegerField(null=True, blank=True,
|
||||
verbose_name=_('Minimum length'),
|
||||
help_text=_(
|
||||
'Currently not supported in our apps and during check-in'
|
||||
))
|
||||
valid_string_length_max = models.PositiveIntegerField(null=True, blank=True,
|
||||
verbose_name=_('Maximum length'),
|
||||
help_text=_(
|
||||
@@ -1885,6 +1888,11 @@ class Question(LoggedModel):
|
||||
else:
|
||||
raise ValidationError(_('Unknown country code.'))
|
||||
elif self.type in (Question.TYPE_STRING, Question.TYPE_TEXT):
|
||||
if self.valid_string_length_min is not None and len(answer) < self.valid_string_length_min:
|
||||
raise ValidationError(MinLengthValidator.message % {
|
||||
'limit_value': self.valid_string_length_min,
|
||||
'show_value': len(answer)
|
||||
})
|
||||
if self.valid_string_length_max is not None and len(answer) > self.valid_string_length_max:
|
||||
raise ValidationError(MaxLengthValidator.message % {
|
||||
'limit_value': self.valid_string_length_max,
|
||||
@@ -1906,6 +1914,8 @@ class Question(LoggedModel):
|
||||
raise ValidationError(_("The maximum date must not be before the minimum value."))
|
||||
if self.valid_number_max and self.valid_number_min and self.valid_number_min > self.valid_number_max:
|
||||
raise ValidationError(_("The maximum value must not be lower than the minimum value."))
|
||||
if self.valid_string_length_max and self.valid_string_length_min and self.valid_string_length_min > self.valid_string_length_max:
|
||||
raise ValidationError(_("The maximum length must not be shorter than the minimum length."))
|
||||
super().clean()
|
||||
|
||||
def clean_type_change(self, old_type, new_type):
|
||||
|
||||
@@ -626,42 +626,14 @@ class Order(LockModel, LoggedModel):
|
||||
self.save(update_fields=['last_modified'])
|
||||
|
||||
def set_expires(self, now_dt=None, subevents=None):
|
||||
now_dt = now_dt or now()
|
||||
tz = ZoneInfo(self.event.settings.timezone)
|
||||
mode = self.event.settings.get('payment_term_mode')
|
||||
if mode == 'days':
|
||||
exp_by_date = now_dt.astimezone(tz) + timedelta(days=self.event.settings.get('payment_term_days', as_type=int))
|
||||
exp_by_date = exp_by_date.astimezone(tz).replace(hour=23, minute=59, second=59, microsecond=0)
|
||||
if self.event.settings.get('payment_term_weekdays'):
|
||||
if exp_by_date.weekday() == 5:
|
||||
exp_by_date += timedelta(days=2)
|
||||
elif exp_by_date.weekday() == 6:
|
||||
exp_by_date += timedelta(days=1)
|
||||
elif mode == 'minutes':
|
||||
exp_by_date = now_dt.astimezone(tz) + timedelta(minutes=self.event.settings.get('payment_term_minutes', as_type=int))
|
||||
else:
|
||||
raise ValueError("'payment_term_mode' has an invalid value '{}'.".format(mode))
|
||||
from pretix.base.services.payment import compute_payment_deadline
|
||||
|
||||
self.expires = exp_by_date
|
||||
|
||||
term_last = self.event.settings.get('payment_term_last', as_type=RelativeDateWrapper)
|
||||
if term_last:
|
||||
if self.event.has_subevents and subevents:
|
||||
terms = [
|
||||
term_last.datetime(se).date()
|
||||
for se in subevents
|
||||
]
|
||||
if not terms:
|
||||
return
|
||||
term_last = min(terms)
|
||||
else:
|
||||
term_last = term_last.datetime(self.event).date()
|
||||
term_last = make_aware(datetime.combine(
|
||||
term_last,
|
||||
time(hour=23, minute=59, second=59)
|
||||
), tz)
|
||||
if term_last < self.expires:
|
||||
self.expires = term_last
|
||||
self.expires = compute_payment_deadline(
|
||||
event=self.event,
|
||||
sales_channel=self.sales_channel,
|
||||
now_dt=now_dt,
|
||||
subevents=subevents,
|
||||
)
|
||||
|
||||
@cached_property
|
||||
def tax_total(self):
|
||||
|
||||
+41
-35
@@ -801,6 +801,18 @@ def generate_compressed_addon_list(op, order, event, only_checked_in=False):
|
||||
return addonlist
|
||||
|
||||
|
||||
def get_sizebox(page: pypdf.PageObject):
|
||||
mediabox = page.mediabox
|
||||
cropbox = page.cropbox
|
||||
|
||||
return pypdf.generic.RectangleObject((
|
||||
max(mediabox[0], cropbox[0]),
|
||||
max(mediabox[1], cropbox[1]),
|
||||
min(mediabox[2], cropbox[2]),
|
||||
min(mediabox[3], cropbox[3]),
|
||||
))
|
||||
|
||||
|
||||
class Renderer:
|
||||
|
||||
def __init__(self, event, layout, background_file):
|
||||
@@ -1079,7 +1091,7 @@ class Renderer:
|
||||
fontsize = float(o['fontsize'])
|
||||
height = float(o['height']) * mm
|
||||
width = float(o['width']) * mm
|
||||
while True:
|
||||
for _i in range(25): # try adapting the font size at most 25 times
|
||||
p, ad, lineheight = self._text_paragraph(op, order, o, override_fontsize=fontsize)
|
||||
w, h = p.wrapOn(canvas, width, 1000 * mm)
|
||||
widths = p.getActualLineWidths0()
|
||||
@@ -1153,11 +1165,10 @@ class Renderer:
|
||||
elif o['type'] == "poweredby":
|
||||
self._draw_poweredby(canvas, op, o)
|
||||
if self.bg_pdf:
|
||||
page_size = (
|
||||
self.bg_pdf.pages[0].mediabox[2] - self.bg_pdf.pages[0].mediabox[0],
|
||||
self.bg_pdf.pages[0].mediabox[3] - self.bg_pdf.pages[0].mediabox[1]
|
||||
)
|
||||
if self.bg_pdf.pages[0].get('/Rotate') in (90, 270):
|
||||
first_page = self.bg_pdf.pages[0]
|
||||
sizebox = get_sizebox(first_page)
|
||||
page_size = (sizebox.width, sizebox.height)
|
||||
if first_page.rotation in (90, 270):
|
||||
# swap dimensions due to pdf being rotated
|
||||
page_size = page_size[::-1]
|
||||
canvas.setPageSize(page_size)
|
||||
@@ -1232,9 +1243,7 @@ class Renderer:
|
||||
|
||||
for i, page in enumerate(fg_pdf.pages):
|
||||
bg_page = self.bg_pdf.pages[i]
|
||||
_correct_page_media_box(bg_page)
|
||||
page.merge_page(bg_page, over=False)
|
||||
output.add_page(page)
|
||||
_merge_with_correct_page_media_box(output, page, bg_page)
|
||||
|
||||
# pdf_header is a string like "%pdf-X.X"
|
||||
if float(self.bg_pdf.pdf_header[5:]) > float(fg_pdf.pdf_header[5:]):
|
||||
@@ -1299,39 +1308,36 @@ def merge_background(fg_pdf: PdfWriter, bg_pdf: PdfWriter, out_file, compress):
|
||||
bg_pdf.write(bg_filename)
|
||||
subprocess.run(pdftk_cmd, check=True, stdout=out_file)
|
||||
else:
|
||||
output = PdfWriter()
|
||||
for i, page in enumerate(fg_pdf.pages):
|
||||
bg_page = bg_pdf.pages[i]
|
||||
_correct_page_media_box(bg_page)
|
||||
page.merge_page(bg_page, over=False)
|
||||
_merge_with_correct_page_media_box(output, page, bg_page)
|
||||
|
||||
# pdf_header is a string like "%pdf-X.X"
|
||||
if float(bg_pdf.pdf_header[5:]) > float(fg_pdf.pdf_header[5:]):
|
||||
fg_pdf.pdf_header = bg_pdf.pdf_header
|
||||
|
||||
fg_pdf.write(out_file)
|
||||
output.pdf_header = (
|
||||
bg_pdf.pdf_header
|
||||
if float(bg_pdf.pdf_header[5:]) > float(fg_pdf.pdf_header[5:])
|
||||
else fg_pdf.pdf_header
|
||||
)
|
||||
output.write(out_file)
|
||||
|
||||
|
||||
def _correct_page_media_box(page: pypdf.PageObject):
|
||||
if page.rotation != 0:
|
||||
page.transfer_rotation_to_content()
|
||||
media_box = page.mediabox
|
||||
def _merge_with_correct_page_media_box(output: pypdf.PdfWriter, fg_page: pypdf.PageObject, bg_page: pypdf.PageObject):
|
||||
"""
|
||||
Adds fg_page to output, merging bg_page behind it.
|
||||
|
||||
If bg_page has a non-zero mergebox/cropbox or is rotated via /Rotate, a transformation is applied to fix this."""
|
||||
trsf = pypdf.Transformation()
|
||||
if media_box.bottom != 0:
|
||||
trsf = trsf.translate(0, -media_box.bottom)
|
||||
if media_box.left != 0:
|
||||
trsf = trsf.translate(-media_box.left, 0)
|
||||
page.add_transformation(trsf, False)
|
||||
for b in ["/MediaBox", "/CropBox", "/BleedBox", "/TrimBox", "/ArtBox"]:
|
||||
if b in page:
|
||||
rr = pypdf.generic.RectangleObject(page[b])
|
||||
pt1 = trsf.apply_on(rr.lower_left)
|
||||
pt2 = trsf.apply_on(rr.upper_right)
|
||||
page[pypdf.generic.NameObject(b)] = pypdf.generic.RectangleObject((
|
||||
min(pt1[0], pt2[0]),
|
||||
min(pt1[1], pt2[1]),
|
||||
max(pt1[0], pt2[0]),
|
||||
max(pt1[1], pt2[1]),
|
||||
))
|
||||
if bg_page.rotation != 0:
|
||||
trsf = trsf.rotate(-bg_page.rotation)
|
||||
|
||||
mb = get_sizebox(bg_page)
|
||||
pt1 = trsf.apply_on(mb.lower_left)
|
||||
pt2 = trsf.apply_on(mb.upper_right)
|
||||
trsf = trsf.translate(-min(pt1[0], pt2[0]), -min(pt1[1], pt2[1]))
|
||||
|
||||
fg_page = output.add_page(fg_page)
|
||||
fg_page.merge_transformed_page(bg_page, trsf, over=False, expand=False)
|
||||
|
||||
|
||||
@deconstructible
|
||||
|
||||
@@ -742,9 +742,9 @@ class RelativeDateField(RelativeDateTimeField):
|
||||
if data.rel_days_number is None or not data.rel_days_relationto:
|
||||
raise ValidationError(self.error_messages['incomplete'])
|
||||
elif data.rel_days_relation == "before" and not choice.supports_before:
|
||||
raise ValidationError(_("A relative date cannot be expressed as 'before' for '{}'".format(choice.text)))
|
||||
raise ValidationError(_('A relative date cannot be expressed as "before" for "{}"'.format(choice.text)))
|
||||
elif data.rel_days_relation == "after" and not choice.supports_after:
|
||||
raise ValidationError(_("A relative date cannot be expressed as 'after' for '{}'".format(choice.text)))
|
||||
raise ValidationError(_('A relative date cannot be expressed as "after" for "{}"'.format(choice.text)))
|
||||
|
||||
return forms.MultiValueField.clean(self, value)
|
||||
|
||||
|
||||
@@ -1605,6 +1605,7 @@ def add_payment_to_cart_session(cart_session, provider, min_value: Decimal=None,
|
||||
'max_value': str(max_value) if max_value is not None else None,
|
||||
'info_data': info_data or {},
|
||||
})
|
||||
cart_session['payments_postpone'] = False
|
||||
|
||||
|
||||
def add_payment_to_cart(request, provider, min_value: Decimal=None, max_value: Decimal=None, info_data: dict=None):
|
||||
|
||||
@@ -54,7 +54,7 @@ from celery.exceptions import MaxRetriesExceededError
|
||||
from django.conf import settings
|
||||
from django.core.files.storage import default_storage
|
||||
from django.core.mail import EmailMultiAlternatives, SafeMIMEMultipart
|
||||
from django.core.mail.message import SafeMIMEText
|
||||
from django.core.mail.message import SafeMIMEText, utf8_charset_qp
|
||||
from django.db import connection, transaction
|
||||
from django.db.models import Q
|
||||
from django.dispatch import receiver
|
||||
@@ -380,6 +380,8 @@ def mail(email: Union[str, Sequence[str]], subject: Union[str, FormattedString],
|
||||
|
||||
|
||||
class CustomEmail(EmailMultiAlternatives):
|
||||
encoding = utf8_charset_qp
|
||||
|
||||
def _create_mime_attachment(self, content, mimetype):
|
||||
"""
|
||||
Convert the content, mimetype pair into a MIME attachment object.
|
||||
@@ -449,9 +451,9 @@ def mail_send_task(self, **kwargs) -> bool:
|
||||
|
||||
# Rewrite all <img> tags from real URLs or data URLs to inline attachments referred to by content ID
|
||||
if outgoing_mail.body_html is not None:
|
||||
html_message = SafeMIMEMultipart(_subtype='related', encoding=settings.DEFAULT_CHARSET)
|
||||
html_message = SafeMIMEMultipart(_subtype='related')
|
||||
html_with_cid, cid_images = replace_images_with_cid_paths(outgoing_mail.body_html)
|
||||
html_message.attach(SafeMIMEText(html_with_cid, 'html', settings.DEFAULT_CHARSET))
|
||||
html_message.attach(SafeMIMEText(html_with_cid, 'html', utf8_charset_qp))
|
||||
attach_cid_images(html_message, cid_images, verify_ssl=True)
|
||||
email.attach_alternative(html_message, "multipart/related")
|
||||
|
||||
|
||||
@@ -961,7 +961,7 @@ def _check_positions(event: Event, now_dt: datetime, time_machine_now_dt: dateti
|
||||
|
||||
|
||||
def _apply_rounding_and_fees(positions: List[CartPosition], payment_requests: List[dict], address: InvoiceAddress,
|
||||
meta_info: dict, event: Event, require_approval=False):
|
||||
meta_info: dict, event: Event, sales_channel: SalesChannel, require_approval=False):
|
||||
fees = []
|
||||
# Pre-rounding, pre-fee total is used for fee calculation
|
||||
total = sum([c.gross_price_before_rounding for c in positions])
|
||||
@@ -1021,7 +1021,14 @@ def _apply_rounding_and_fees(positions: List[CartPosition], payment_requests: Li
|
||||
payments_assigned += to_pay
|
||||
p['payment_amount'] = to_pay
|
||||
|
||||
if total != payments_assigned and not require_approval:
|
||||
allow_postponed_payment = (
|
||||
require_approval or
|
||||
(
|
||||
sales_channel.identifier in event.settings.payment_choice_postpone_allowed_channels and not payment_requests
|
||||
)
|
||||
)
|
||||
|
||||
if total != payments_assigned and not allow_postponed_payment:
|
||||
raise OrderError(_("The selected payment methods do not cover the total balance."))
|
||||
|
||||
return fees
|
||||
@@ -1043,7 +1050,15 @@ def _create_order(event: Event, *, email: str, positions: List[CartPosition], no
|
||||
|
||||
# Final calculation of fees, also performs final rounding
|
||||
try:
|
||||
fees = _apply_rounding_and_fees(positions, payment_requests, address, meta_info, event, require_approval=require_approval)
|
||||
fees = _apply_rounding_and_fees(
|
||||
positions,
|
||||
payment_requests,
|
||||
address,
|
||||
meta_info,
|
||||
event,
|
||||
sales_channel=sales_channel,
|
||||
require_approval=require_approval
|
||||
)
|
||||
except TaxRule.SaleNotAllowed:
|
||||
raise OrderError(error_messages['country_blocked'])
|
||||
|
||||
@@ -3469,7 +3484,7 @@ def change_payment_provider(order: Order, payment_provider, amount=None, new_pay
|
||||
}
|
||||
)
|
||||
|
||||
new_invoice_created = False
|
||||
new_invoice = None
|
||||
if recreate_invoices:
|
||||
# Lock to prevent duplicate invoice creation
|
||||
order = Order.objects.select_for_update(of=OF_SELF).get(pk=order.pk)
|
||||
@@ -3480,13 +3495,16 @@ def change_payment_provider(order: Order, payment_provider, amount=None, new_pay
|
||||
if has_active_invoice and order.total != oldtotal:
|
||||
try:
|
||||
generate_cancellation(i)
|
||||
generate_invoice(order)
|
||||
new_invoice = generate_invoice(order)
|
||||
except Exception as e:
|
||||
logger.exception("Could not generate invoice.")
|
||||
order.log_action("pretix.event.order.invoice.failed", data={
|
||||
"exception": str(e)
|
||||
})
|
||||
new_invoice_created = True
|
||||
else:
|
||||
order.log_action('pretix.event.order.invoice.generated', data={
|
||||
'invoice': new_invoice.pk
|
||||
})
|
||||
|
||||
elif (not has_active_invoice or order.invoice_dirty) and invoice_qualified(order):
|
||||
if order.event.settings.get('invoice_generate') == 'True' or (
|
||||
@@ -3496,10 +3514,9 @@ def change_payment_provider(order: Order, payment_provider, amount=None, new_pay
|
||||
try:
|
||||
if has_active_invoice:
|
||||
generate_cancellation(i)
|
||||
i = generate_invoice(order)
|
||||
new_invoice_created = True
|
||||
new_invoice = generate_invoice(order)
|
||||
order.log_action('pretix.event.order.invoice.generated', data={
|
||||
'invoice': i.pk
|
||||
'invoice': new_invoice.pk
|
||||
})
|
||||
except Exception as e:
|
||||
logger.exception("Could not generate invoice.")
|
||||
@@ -3507,8 +3524,11 @@ def change_payment_provider(order: Order, payment_provider, amount=None, new_pay
|
||||
"exception": str(e)
|
||||
})
|
||||
|
||||
if new_invoice and invoice_transmission_separately(new_invoice):
|
||||
transmit_invoice.apply_async(args=(order.event_id, new_invoice.pk, False))
|
||||
|
||||
order.create_transactions()
|
||||
return old_fee, new_fee, fee, new_payment, new_invoice_created
|
||||
return old_fee, new_fee, fee, new_payment, bool(new_invoice)
|
||||
|
||||
|
||||
@receiver(order_paid, dispatch_uid="pretixbase_order_paid_giftcards")
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
from datetime import datetime, time, timedelta
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
from django.utils.timezone import make_aware, now
|
||||
|
||||
from pretix.base.models import Event, SalesChannel
|
||||
from pretix.base.reldate import RelativeDateWrapper
|
||||
|
||||
|
||||
def compute_payment_deadline(event: Event, sales_channel: SalesChannel, now_dt=None, subevents=None) -> datetime:
|
||||
now_dt = now_dt or now()
|
||||
tz = ZoneInfo(event.settings.timezone)
|
||||
|
||||
sales_channel_suffix = "_" + sales_channel.identifier.replace(".", "_")
|
||||
if not (mode := event.settings.get(f'payment_term_mode{sales_channel_suffix}')):
|
||||
mode = event.settings.get('payment_term_mode')
|
||||
sales_channel_suffix = ""
|
||||
|
||||
if mode == 'days':
|
||||
exp_by_date = now_dt.astimezone(tz) + timedelta(
|
||||
days=event.settings.get(f'payment_term_days{sales_channel_suffix}', as_type=int))
|
||||
exp_by_date = exp_by_date.astimezone(tz).replace(hour=23, minute=59, second=59, microsecond=0)
|
||||
if event.settings.get('payment_term_weekdays'):
|
||||
if exp_by_date.weekday() == 5:
|
||||
exp_by_date += timedelta(days=2)
|
||||
elif exp_by_date.weekday() == 6:
|
||||
exp_by_date += timedelta(days=1)
|
||||
elif mode == 'minutes':
|
||||
exp_by_date = now_dt.astimezone(tz) + timedelta(
|
||||
minutes=event.settings.get(f'payment_term_minutes{sales_channel_suffix}', as_type=int))
|
||||
else:
|
||||
raise ValueError("'payment_term_mode' has an invalid value '{}'.".format(mode))
|
||||
|
||||
expires = exp_by_date
|
||||
|
||||
term_last = event.settings.get('payment_term_last', as_type=RelativeDateWrapper)
|
||||
if term_last:
|
||||
if event.has_subevents and subevents:
|
||||
terms = [
|
||||
term_last.datetime(se).date()
|
||||
for se in subevents
|
||||
]
|
||||
if not terms:
|
||||
return expires
|
||||
term_last = min(terms)
|
||||
else:
|
||||
term_last = term_last.datetime(event).date()
|
||||
term_last = make_aware(datetime.combine(
|
||||
term_last,
|
||||
time(hour=23, minute=59, second=59)
|
||||
), tz)
|
||||
if term_last < expires:
|
||||
return term_last
|
||||
|
||||
return expires
|
||||
@@ -561,6 +561,22 @@ def base_placeholders(sender, **kwargs):
|
||||
'event_location', ['event_or_subevent'], lambda event_or_subevent: str(event_or_subevent.location or ''),
|
||||
lambda event: str(event.location or ''),
|
||||
),
|
||||
SimpleFunctionalTextPlaceholder(
|
||||
'event_begin_time', ['event_or_subevent'],
|
||||
lambda event_or_subevent:
|
||||
date_format(event_or_subevent.date_from.astimezone(event_or_subevent.timezone), 'TIME_FORMAT')
|
||||
if event_or_subevent.date_from
|
||||
else '',
|
||||
lambda event: date_format(event.date_from.astimezone(event.timezone), 'TIME_FORMAT') if event.date_from else '',
|
||||
),
|
||||
SimpleFunctionalTextPlaceholder(
|
||||
'event_end_time', ['event_or_subevent'],
|
||||
lambda event_or_subevent:
|
||||
date_format(event_or_subevent.date_to.astimezone(event_or_subevent.timezone), 'TIME_FORMAT')
|
||||
if event_or_subevent.date_to
|
||||
else '',
|
||||
lambda event: date_format(event.date_to.astimezone(event.timezone), 'TIME_FORMAT') if event.date_to else '',
|
||||
),
|
||||
SimpleFunctionalTextPlaceholder(
|
||||
'event_admission_time', ['event_or_subevent'],
|
||||
lambda event_or_subevent:
|
||||
|
||||
@@ -343,6 +343,66 @@ def _validate_vat_id_EU(vat_id, country_code):
|
||||
return vat_id
|
||||
|
||||
|
||||
def _validate_vat_id_EU_fallback_germany(vat_id, country_code, requester_id):
|
||||
# We can skip most static validation checks because _validate_vat_id_EU always runs before
|
||||
vat_id = normalize_vat_id(vat_id, country_code)
|
||||
|
||||
# The VIES service of the European commission is overused and down due to rate limits A LOT. There is another
|
||||
# API by German BZSt, but it only works if the requester is German and the requested is not.
|
||||
# https://www.bzst.de/DE/Unternehmen/Identifikationsnummern/Umsatzsteuer-Identifikationsnummer/AuslaendischeUSt-IdNr/auslaendische_ust_idnr_node.html
|
||||
try:
|
||||
r = requests.post(
|
||||
"https://api.evatr.vies.bzst.de/app/v1/abfrage",
|
||||
json={
|
||||
"anfragendeUstid": requester_id,
|
||||
"angefragteUstid": vat_id,
|
||||
},
|
||||
timeout=10,
|
||||
)
|
||||
d = r.json()
|
||||
if r.status_code == 200:
|
||||
if d['status'] in ('evatr-0000', 'evatr-2008'):
|
||||
# evatr-0000: Die angefragte Ust-IdNr. ist zum Anfragezeitpunkt gültig.
|
||||
# evatr-2008: Die angefragte Ust-IdNr. ist zum Anfragezeitpunkt gültig.
|
||||
# Für die qualifizierte Bestätigungsanfrage liegt einer Besonderheit vor.
|
||||
# Für Rückfragen wenden Sie sich an das BZSt.
|
||||
return vat_id
|
||||
# evatr-2002: Die angefragte USt-IdNr. ist zum Anfragezeitpunkt nicht gültig.
|
||||
# Sie ist erst gültig ab dem Datum im Feld gueltigAb.
|
||||
# evatr-2006: Die angefragte Ust-IdNr. ist zum Anfragezeitpunkt nicht gültig.
|
||||
# Sie war gültig im Zeitraum, der durch die Werte in den Feldern gueltigAb und gueltigBis beschrieben ist.
|
||||
raise VATIDFinalError(error_messages['invalid'])
|
||||
elif r.status_code == 400:
|
||||
if d['status'] in ('evatr-0002', 'evatr-0004', 'evatr-0008'):
|
||||
# evatr-0002: Mindestens eins der Pflichtfelder ist nicht besetzt.
|
||||
# evatr-0004: Die anfragende DE Ust-IdNr. ist syntaktisch falsch. Sie passt nicht in das deutsche Erzeugungsschema.
|
||||
# evatr-0008: Die maximale Anzahl von qualifizierten Bestätigungsabfragen für diese Session wurde erreicht.
|
||||
# Bitte starten Sie erneut mit einer einfachen Bestätigungsabfrage.
|
||||
raise VATIDTemporaryError(error_messages['unavailable'])
|
||||
# evatr-0005: Die angegebene angefragte Ust-IdNr. ist syntaktisch falsch.
|
||||
# evatr-0012: Die angefrage USt-IdNr. ist syntaktisch falsch. Sie passt nicht in das Erzeugungsschema.
|
||||
# evatr-2003: Das angegebene Länderkennzeichen der angefragten USt-IdNr. ist nicht gültig.
|
||||
raise VATIDFinalError(error_messages['invalid'])
|
||||
elif r.status_code == 403:
|
||||
# evatr-0006: Die anfragende DE USt-IdNr. ist nicht berechtigt eine DE Ust-IdNr. anzufragen.
|
||||
# evatr-0007: Fehlerhafter Aufruf.
|
||||
raise VATIDTemporaryError(error_messages['unavailable'])
|
||||
elif r.status_code == 404:
|
||||
if d['status'] in ('evatr-2005'):
|
||||
# evatr-2005: Die angegebene eigene DE Ust-IdNr. ist zum Anfragezeitpunkt nicht gültig.
|
||||
raise VATIDTemporaryError(error_messages['unavailable'])
|
||||
# evatr-2001: Die angefragte USt-IdNr. ist zum Anfragezeitpunkt nicht vergeben.
|
||||
raise VATIDFinalError(error_messages['invalid'])
|
||||
else: # 500, 503
|
||||
raise VATIDTemporaryError(error_messages['unavailable'])
|
||||
except requests.RequestException:
|
||||
logger.exception('VAT ID checking failed for country {}'.format(country_code))
|
||||
raise VATIDTemporaryError(error_messages['unavailable'])
|
||||
except ValueError: # JSON parsing failed
|
||||
logger.exception('VAT ID checking failed for country {}'.format(country_code))
|
||||
raise VATIDTemporaryError(error_messages['unavailable'])
|
||||
|
||||
|
||||
def _validate_vat_id_CH(vat_id, country_code):
|
||||
if vat_id[:3] != 'CHE':
|
||||
raise VATIDFinalError(error_messages['country_mismatch'])
|
||||
@@ -394,12 +454,18 @@ def _validate_vat_id_CH(vat_id, country_code):
|
||||
return vat_id
|
||||
|
||||
|
||||
def validate_vat_id(vat_id, country_code):
|
||||
def validate_vat_id(vat_id, country_code, requester_id=None):
|
||||
if not vat_id:
|
||||
return vat_id
|
||||
country_code = str(country_code)
|
||||
if is_eu_country(country_code):
|
||||
return _validate_vat_id_EU(vat_id, country_code)
|
||||
try:
|
||||
return _validate_vat_id_EU(vat_id, country_code)
|
||||
except VATIDTemporaryError:
|
||||
if requester_id and requester_id.startswith("DE") and not vat_id.startswith("DE"):
|
||||
return _validate_vat_id_EU_fallback_germany(vat_id, country_code, requester_id)
|
||||
else:
|
||||
raise
|
||||
elif country_code == 'CH':
|
||||
return _validate_vat_id_CH(vat_id, country_code)
|
||||
elif country_code == 'NO':
|
||||
|
||||
+46
-20
File diff suppressed because one or more lines are too long
+22
-10
@@ -50,8 +50,8 @@ from pretix.api.serializers.order import (
|
||||
from pretix.api.serializers.waitinglist import WaitingListSerializer
|
||||
from pretix.base.i18n import LazyLocaleException
|
||||
from pretix.base.models import (
|
||||
CachedCombinedTicket, CachedTicket, Event, InvoiceAddress, OrderPayment,
|
||||
OrderPosition, OrderRefund, OutgoingMail, QuestionAnswer,
|
||||
CachedCombinedTicket, CachedTicket, Event, Invoice, InvoiceAddress,
|
||||
OrderPayment, OrderPosition, OrderRefund, OutgoingMail, QuestionAnswer,
|
||||
)
|
||||
from pretix.base.services.invoices import invoice_pdf_task
|
||||
from pretix.base.signals import register_data_shredders
|
||||
@@ -598,18 +598,30 @@ class InvoiceShredder(BaseDataShredder):
|
||||
def shred_data(self, progress_callback=None):
|
||||
qs_i = self.event.invoices.filter(shredded=False)
|
||||
total = qs_i.count()
|
||||
ignore_fields = (
|
||||
'prefix', 'invoice_no', 'full_invoice_no', 'invoice_from', 'invoice_from_name', 'invoice_from_zipcode',
|
||||
'invoice_from_city', 'invoice_from_state', 'invoice_from_country', 'invoice_from_tax_id',
|
||||
'invoice_from_vat_id', 'locale', 'payment_provider_stamp', 'footer_text', 'foreign_currency_display',
|
||||
'foreign_currency_source', 'transmission_type', 'transmission_provider', 'transmission_status',
|
||||
)
|
||||
|
||||
for i in _progress_helper(qs_i, progress_callback, 0, total):
|
||||
if i.file:
|
||||
i.file.delete()
|
||||
i.shredded = True
|
||||
i.introductory_text = "█"
|
||||
i.additional_text = "█"
|
||||
i.invoice_to = "█"
|
||||
i.payment_provider_text = "█"
|
||||
i.transmission_info = {"_shredded": True}
|
||||
i.save()
|
||||
i.lines.update(description="█")
|
||||
i.shredded = True
|
||||
|
||||
for f in Invoice._meta.fields:
|
||||
if f.name in ignore_fields:
|
||||
continue
|
||||
val = getattr(i, f.name, None)
|
||||
if val and isinstance(val, str):
|
||||
setattr(i, f.name, "█")
|
||||
elif val and isinstance(val, list): # jsonfield
|
||||
setattr(i, f.name, [])
|
||||
elif val and isinstance(val, dict): # jsonfield
|
||||
setattr(i, f.name, {"_shredded": True})
|
||||
i.save()
|
||||
i.lines.update(description="█", attendee_name="█")
|
||||
|
||||
|
||||
class CachedTicketShredder(BaseDataShredder):
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
from decimal import ROUND_HALF_UP, Decimal
|
||||
from typing import Optional
|
||||
|
||||
from babel import Locale, UnknownLocaleError
|
||||
from babel.numbers import format_currency
|
||||
@@ -35,32 +36,32 @@ register = template.Library()
|
||||
|
||||
|
||||
@register.filter("money")
|
||||
def money_filter(value: Decimal, arg='', hide_currency=False):
|
||||
if isinstance(value, (float, int)):
|
||||
def money_filter(value: Optional[Decimal | float | int | str], arg='', hide_currency=False):
|
||||
if isinstance(value, (float, int, str)):
|
||||
if value == '':
|
||||
return value
|
||||
value = Decimal(value)
|
||||
if value is None:
|
||||
value = Decimal('0.00')
|
||||
if not isinstance(value, Decimal):
|
||||
if value == '':
|
||||
return value
|
||||
raise TypeError("Invalid data type passed to money filter: %r" % type(value))
|
||||
if not arg:
|
||||
raise ValueError("No currency passed.")
|
||||
arg = arg.upper()
|
||||
|
||||
places = settings.CURRENCY_PLACES.get(arg, 2)
|
||||
rounded = value.quantize(Decimal('1') / 10 ** places, ROUND_HALF_UP)
|
||||
if places < 2 and rounded != value:
|
||||
# We display decimal places even if we shouldn't for this currency if rounding
|
||||
# would make the numbers incorrect. If this branch executes, it's likely a bug in
|
||||
# pretix, but we won't show wrong numbers!
|
||||
if hide_currency:
|
||||
return floatformat(value, "2g")
|
||||
else:
|
||||
return '{} {}'.format(arg, floatformat(value, "2g"))
|
||||
if value.normalize().as_tuple().exponent < -9:
|
||||
# Heuristic: It's unlikely we'll ever see values of less than 0.000000001 in any currency. Therefore, if we
|
||||
# do see them, we very likely deal with a floating point error. This happens mostly in dev mode when computations
|
||||
# are made in SQLite, which uses REAL precision, but it can also happen when we naively pass a float from Python
|
||||
# land to this filter (even though it should not happen).
|
||||
value = value.quantize(Decimal('1e-9'), ROUND_HALF_UP).normalize()
|
||||
|
||||
currency_places = settings.CURRENCY_PLACES.get(arg, 2)
|
||||
required_places = -value.normalize().as_tuple().exponent
|
||||
render_places = max(currency_places, required_places)
|
||||
|
||||
if hide_currency:
|
||||
return floatformat(value, f"{places}g")
|
||||
return floatformat(value, f"{render_places}g")
|
||||
|
||||
try:
|
||||
locale = Locale(get_babel_locale())
|
||||
@@ -68,14 +69,29 @@ def money_filter(value: Decimal, arg='', hide_currency=False):
|
||||
locale = "en"
|
||||
|
||||
try:
|
||||
return format_currency(value, arg, locale=locale)
|
||||
return format_currency(
|
||||
value,
|
||||
arg,
|
||||
locale=locale,
|
||||
# We only allow Babel to restrict the digits to the digits defined by the currency if this does not remove any
|
||||
# precision in case we have sub-currency precision (which we shouldn't have in most places, but it's still
|
||||
# better than showing wrong data). Note: Weird precision effects can occur after in-database arithmetic
|
||||
# on SQLite, since SQLite does not have fixed-decimal computation.
|
||||
currency_digits=currency_places >= required_places,
|
||||
decimal_quantization=currency_places >= required_places,
|
||||
)
|
||||
except:
|
||||
return '{} {}'.format(arg, floatformat(value, f"{places}g"))
|
||||
return '{} {}'.format(arg, floatformat(value, f"{render_places}g"))
|
||||
|
||||
|
||||
@register.filter("money_without_currency")
|
||||
def money_filter_without_currency(value: Optional[Decimal | float | int | str], arg=''):
|
||||
return money_filter(value, arg, hide_currency=True)
|
||||
|
||||
|
||||
@register.filter("money_numberfield")
|
||||
def money_numberfield_filter(value: Decimal, arg=''):
|
||||
if isinstance(value, (float, int)):
|
||||
def money_numberfield_filter(value: Optional[Decimal | float | int | str], arg=''):
|
||||
if isinstance(value, (float, int, str)):
|
||||
value = Decimal(value)
|
||||
if not isinstance(value, Decimal):
|
||||
raise TypeError("Invalid data type passed to money filter: %r" % type(value))
|
||||
@@ -87,15 +103,28 @@ def money_numberfield_filter(value: Decimal, arg=''):
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
def tax_rate_format(number):
|
||||
def tax_rate_format(number: Optional[Decimal | float | int | str]):
|
||||
"""
|
||||
Display a Decimal to its significant decimal places, used for tax rates.
|
||||
"""
|
||||
assert isinstance(number, Decimal)
|
||||
if isinstance(number, (float, int, str)):
|
||||
if number == '':
|
||||
return number
|
||||
number = Decimal(number)
|
||||
if number is None:
|
||||
number = Decimal('0.00')
|
||||
if not isinstance(number, Decimal):
|
||||
raise TypeError("Invalid data type passed to tax rate format filter: %r" % type(number))
|
||||
if number.normalize().as_tuple().exponent < -9:
|
||||
# Heuristic: It's unlikely we'll ever see values of less than 0.000000001 in any currency. Therefore, if we
|
||||
# do see them, we very likely deal with a floating point error. This happens mostly in dev mode when computations
|
||||
# are made in SQLite, which uses REAL precision, but it can also happen when we naively pass a float from Python
|
||||
# land to this filter (even though it should not happen).
|
||||
number = number.quantize(Decimal('1e-9'), ROUND_HALF_UP).normalize()
|
||||
return mark_safe(
|
||||
formats.number_format(
|
||||
number.normalize(),
|
||||
-number.as_tuple().exponent,
|
||||
number,
|
||||
-number.normalize().as_tuple().exponent,
|
||||
use_l10n=True,
|
||||
force_grouping=False,
|
||||
)
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
#
|
||||
import contextvars
|
||||
from contextlib import contextmanager
|
||||
from datetime import datetime
|
||||
|
||||
from dateutil.parser import parse
|
||||
from django.utils.timezone import now
|
||||
|
||||
from pretix.base.auth import has_event_access_permission
|
||||
@@ -34,7 +34,7 @@ timemachine_now_var = contextvars.ContextVar('timemachine_now', default=None)
|
||||
def time_machine_now_assigned_from_request(request):
|
||||
if hasattr(request, 'event') and f'timemachine_now_dt:{request.event.pk}' in request.session and \
|
||||
request.event.testmode and has_event_access_permission(request):
|
||||
request.now_dt = parse(request.session[f'timemachine_now_dt:{request.event.pk}'])
|
||||
request.now_dt = datetime.fromisoformat(request.session[f'timemachine_now_dt:{request.event.pk}'])
|
||||
request.now_dt_is_fake = True
|
||||
else:
|
||||
request.now_dt = now()
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
from django.conf import settings
|
||||
from django.http import (
|
||||
HttpResponseForbidden, HttpResponseNotFound, HttpResponseServerError,
|
||||
)
|
||||
@@ -27,7 +28,6 @@ from django.template import TemplateDoesNotExist, loader
|
||||
from django.template.loader import get_template
|
||||
from django.utils.functional import Promise
|
||||
from django.utils.translation import gettext as _
|
||||
from sentry_sdk import last_event_id
|
||||
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.middleware import get_language_from_request
|
||||
@@ -106,9 +106,14 @@ def server_error(request):
|
||||
template = loader.get_template('500.html')
|
||||
except TemplateDoesNotExist:
|
||||
return HttpResponseServerError('<h1>Server Error (500)</h1>', content_type='text/html')
|
||||
if settings.SENTRY_ENABLED:
|
||||
from sentry_sdk import last_event_id
|
||||
sentry_id = last_event_id()
|
||||
else:
|
||||
sentry_id = None
|
||||
r = HttpResponseServerError(template.render({
|
||||
'request': request,
|
||||
'sentry_event_id': last_event_id(),
|
||||
'sentry_event_id': sentry_id,
|
||||
}))
|
||||
r.xframe_options_exempt = True
|
||||
return r
|
||||
|
||||
@@ -135,6 +135,8 @@ class BaseQuestionsViewMixin:
|
||||
question_field.initial = getattr(question_field, 'initial', None) or src['initial']
|
||||
if 'validators' in src:
|
||||
question_field.validators += src['validators']
|
||||
if 'label' in src:
|
||||
question_field.label = src['label']
|
||||
|
||||
if len(form.fields) > 0:
|
||||
formlist.append(form)
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
import logging
|
||||
import multiprocessing
|
||||
import os
|
||||
|
||||
from celery import Celery, signals
|
||||
@@ -54,6 +55,21 @@ def on_task_received(sender, request, **kwargs):
|
||||
logger.info(f"Task {request.id} has trace {trace}")
|
||||
|
||||
|
||||
@receiver(signals.after_setup_task_logger)
|
||||
def on_after_setup_task_logger(sender, logger, loglevel, logfile, format, colorize, **kwargs):
|
||||
# This hack seems to be required to get celery to log internal events from eg billiard/pool.py such as
|
||||
# "worker killed because it used too much memory"
|
||||
# You can test that it is working by starting a celery worker with a low value like
|
||||
# --max-memory-per-child 300000
|
||||
# and then trigger a task. Result should look like this:
|
||||
# [2026-09-23 10:42:26,234: WARNING/ForkPoolWorker-16]: [???:???] child process exiting after exceeding memory limit (394540KiB / 300000KiB)
|
||||
# The ???:??? are likely because by copying the handlers, we are also copying the format, but I was unable to find
|
||||
# a better compatible way.
|
||||
multi_logger = multiprocessing.get_logger()
|
||||
multi_logger.setLevel(logging.WARNING)
|
||||
multi_logger.handlers = logger.handlers
|
||||
|
||||
|
||||
@receiver(signals.task_prerun)
|
||||
def on_task_prerun(sender, task_id, task, **kwargs):
|
||||
from pretix.helpers.logs import local
|
||||
|
||||
@@ -172,7 +172,9 @@ class CachedFileInput(forms.ClearableFileInput):
|
||||
from ...base.models import CachedFile
|
||||
v = super().value_from_datadict(data, files, name)
|
||||
if v is None and data.get(name + '-cachedfile'): # An explicit "[x] clear" would be False, not None
|
||||
return CachedFile.objects.filter(id=data[name + '-cachedfile']).first()
|
||||
v = CachedFile.objects.filter(id=data[name + '-cachedfile']).first()
|
||||
if not v.allowed_for_session(self.request):
|
||||
v = None
|
||||
return v
|
||||
|
||||
def get_context(self, name, value, attrs):
|
||||
@@ -244,6 +246,11 @@ class ExtFileField(ExtValidationMixin, SizeFileField):
|
||||
class CachedFileField(ExtFileField):
|
||||
widget = CachedFileInput
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.request = kwargs.pop("request", None)
|
||||
super().__init__(*args, **kwargs)
|
||||
self.widget.request = self.request
|
||||
|
||||
def to_python(self, data):
|
||||
from ...base.models import CachedFile
|
||||
|
||||
@@ -271,6 +278,8 @@ class CachedFileField(ExtFileField):
|
||||
filename=data.name,
|
||||
type=data.content_type,
|
||||
)
|
||||
if self.request:
|
||||
cf.bind_to_session(self.request) # no salt because we want direct web access
|
||||
cf.file.save(data.name, data.file)
|
||||
cf.save()
|
||||
data._uploaded_to = cf
|
||||
@@ -294,6 +303,8 @@ class CachedFileField(ExtFileField):
|
||||
filename=data.name,
|
||||
type=data.content_type,
|
||||
)
|
||||
if self.request:
|
||||
cf.bind_to_session(self.request) # no salt because we want direct web access
|
||||
cf.file.save(data.name, data.file)
|
||||
cf.save()
|
||||
data._uploaded_to = cf
|
||||
|
||||
@@ -400,10 +400,10 @@ class EventMetaValueForm(forms.ModelForm):
|
||||
if self.disabled:
|
||||
self.fields['value'].widget.attrs['readonly'] = 'readonly'
|
||||
|
||||
def clean_slug(self):
|
||||
def clean_value(self):
|
||||
if self.disabled:
|
||||
return self.instance.value if self.instance else None
|
||||
return self.cleaned_data['slug']
|
||||
return self.cleaned_data['value']
|
||||
|
||||
class Meta:
|
||||
model = EventMetaValue
|
||||
@@ -600,6 +600,7 @@ class EventSettingsForm(EventSettingsValidationMixin, FormPlaceholderMixin, Sett
|
||||
'imprint_url',
|
||||
'checkout_email_helptext',
|
||||
'presale_has_ended_text',
|
||||
'event_list_empty_text',
|
||||
'voucher_explanation_text',
|
||||
'checkout_success_text',
|
||||
'show_dates_on_frontpage',
|
||||
@@ -734,6 +735,7 @@ class EventSettingsForm(EventSettingsValidationMixin, FormPlaceholderMixin, Sett
|
||||
del self.fields['event_list_available_only']
|
||||
del self.fields['event_list_filters']
|
||||
del self.fields['event_calendar_future_only']
|
||||
del self.fields['event_list_empty_text']
|
||||
self.fields['primary_font'].choices = [('Open Sans', 'Open Sans')] + sorted([
|
||||
(a, FontSelect.FontOption(title=a, data=v)) for a, v in get_fonts(self.event, pdf_support_required=False).items()
|
||||
], key=lambda a: a[0])
|
||||
@@ -836,9 +838,10 @@ class CancelSettingsForm(SettingsForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if self.obj.settings.giftcard_expiry_years is not None:
|
||||
self.fields['cancel_allow_user_paid_refund_as_giftcard'].help_text = gettext(
|
||||
'You have configured gift cards to be valid {} years plus the year the gift card is issued in.'
|
||||
).format(self.obj.settings.giftcard_expiry_years)
|
||||
self.fields['cancel_allow_user_paid_refund_as_giftcard'].help_text = format_html(
|
||||
gettext('You have configured gift cards to be valid {} years plus the year the gift card is issued in.'),
|
||||
self.obj.settings.giftcard_expiry_years
|
||||
)
|
||||
|
||||
|
||||
class PaymentSettingsForm(EventSettingsValidationMixin, SettingsForm):
|
||||
@@ -853,9 +856,60 @@ class PaymentSettingsForm(EventSettingsValidationMixin, SettingsForm):
|
||||
'payment_term_accept_late',
|
||||
'payment_pending_hidden',
|
||||
'payment_explanation',
|
||||
'payment_choice_postpone_allowed_channels',
|
||||
'tax_rule_payment',
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
channels = list(self.obj.organizer.sales_channels.all())
|
||||
self.fields['payment_choice_postpone_allowed_channels'].choices = [
|
||||
(c.identifier, c.label) for c in channels
|
||||
if c.type_instance.payment_restrictions_supported
|
||||
]
|
||||
|
||||
self.term_channel_fields = {}
|
||||
for c in channels:
|
||||
if c.type_instance.payment_restrictions_supported and c.identifier != "web":
|
||||
# At the moment, it seems sufficient to allow this for the same channel types as other payment settings
|
||||
# We can always introduce more flags later if needed
|
||||
suffix = '_' + c.identifier.replace(".", "_")
|
||||
self.term_channel_fields[c] = [
|
||||
'payment_term_mode' + suffix,
|
||||
'payment_term_days' + suffix,
|
||||
'payment_term_minutes' + suffix,
|
||||
]
|
||||
self.fields['payment_term_mode' + suffix] = DEFAULTS['payment_term_mode']['form_class'](
|
||||
label=_("Payment term"),
|
||||
widget=forms.RadioSelect,
|
||||
required=False,
|
||||
choices=(
|
||||
('', _("same as above")),
|
||||
('days', _("different payment term in days")),
|
||||
('minutes', _("different payment term in minutes"))
|
||||
),
|
||||
)
|
||||
self.fields['payment_term_days' + suffix] = DEFAULTS['payment_term_days']['form_class'](
|
||||
required=False,
|
||||
**DEFAULTS['payment_term_days']['form_kwargs'](suffix, 1),
|
||||
)
|
||||
self.fields['payment_term_minutes' + suffix] = DEFAULTS['payment_term_minutes']['form_class'](
|
||||
required=False,
|
||||
**DEFAULTS['payment_term_minutes']['form_kwargs'](suffix, 2),
|
||||
)
|
||||
|
||||
def clean(self):
|
||||
data = super().clean()
|
||||
for c in self.term_channel_fields.keys():
|
||||
suffix = '_' + c.identifier.replace(".", "_")
|
||||
mode = self.cleaned_data.get(f'payment_term_mode{suffix}')
|
||||
if mode == 'days' and self.cleaned_data.get(f'payment_term_days{suffix}') is None:
|
||||
raise ValidationError({f'payment_term_days{suffix}': _("This field is required.")})
|
||||
if mode == 'minutes' and self.cleaned_data.get(f'payment_term_minutes{suffix}') is None:
|
||||
raise ValidationError({f'payment_term_minutes{suffix}': _("This field is required.")})
|
||||
return data
|
||||
|
||||
def clean_payment_term_days(self):
|
||||
value = self.cleaned_data.get('payment_term_days')
|
||||
if self.cleaned_data.get('payment_term_mode') == 'days' and value is None:
|
||||
@@ -1582,10 +1636,15 @@ class MailSettingsForm(FormPlaceholderMixin, SettingsForm):
|
||||
self._set_field_placeholders(k, v, rich=k.startswith('mail_text_') and k not in self.plain_rendering)
|
||||
|
||||
for k, v in list(self.fields.items()):
|
||||
if k.endswith('_attendee') and not event.settings.attendee_emails_asked:
|
||||
# If we don't ask for attendee emails, we can't send them anything and we don't need to clutter
|
||||
# the user interface with it
|
||||
del self.fields[k]
|
||||
if k.endswith('_attendee'):
|
||||
if not event.settings.attendee_emails_asked:
|
||||
# If we don't ask for attendee emails, we can't send them anything and we don't need to clutter
|
||||
# the user interface with it
|
||||
del self.fields[k]
|
||||
elif 'subject' in k and k.replace("subject", "send") in self.fields:
|
||||
v.widget.attrs["data-display-dependency"] = f'#id_{k.replace("subject", "send")}'
|
||||
elif 'text' in k and k.replace("text", "send") in self.fields:
|
||||
v.widget.attrs["data-display-dependency"] = f'#id_{k.replace("text", "send")}'
|
||||
|
||||
|
||||
class TicketSettingsForm(SettingsForm):
|
||||
|
||||
@@ -105,13 +105,6 @@ class GlobalSettingsForm(SettingsForm):
|
||||
domain=settings.SITE_URL
|
||||
)
|
||||
)),
|
||||
('widget_vite_origins', forms.CharField(
|
||||
widget=forms.Textarea(attrs={'rows': '3'}),
|
||||
required=False,
|
||||
# Not translated on purpose, this is a temporary feature and contains too many special case words
|
||||
label="Vite widget origins",
|
||||
help_text="One origin per line (e.g. https://example.com). Requests from these origins will be served the new vite-based widget.",
|
||||
))
|
||||
])
|
||||
responses = register_global_settings.send(self)
|
||||
for r, response in sorted(responses, key=lambda r: str(r[0])):
|
||||
|
||||
@@ -251,6 +251,7 @@ class QuestionForm(I18nModelForm):
|
||||
'valid_date_min',
|
||||
'valid_date_max',
|
||||
'valid_file_portrait',
|
||||
'valid_string_length_min',
|
||||
'valid_string_length_max',
|
||||
]
|
||||
widgets = {
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
from django import forms
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.functional import lazy
|
||||
from django.utils.html import format_html
|
||||
from django.utils.html import conditional_escape, format_html
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from pretix.base.modelimport_orders import get_order_import_columns
|
||||
@@ -66,7 +66,7 @@ class ProcessForm(forms.Form):
|
||||
widget=forms.Select(
|
||||
attrs={'data-static': 'true'}
|
||||
),
|
||||
help_text=c.help_text,
|
||||
help_text=conditional_escape(c.help_text),
|
||||
)
|
||||
|
||||
def get_columns(self):
|
||||
|
||||
@@ -364,7 +364,7 @@ class TeamForm(forms.ModelForm):
|
||||
for opt in pg.options
|
||||
],
|
||||
label=pg.label,
|
||||
help_text=pg.help_text,
|
||||
help_text=conditional_escape(pg.help_text),
|
||||
initial=initial,
|
||||
widget=forms.RadioSelect,
|
||||
)
|
||||
@@ -389,7 +389,7 @@ class TeamForm(forms.ModelForm):
|
||||
for opt in pg.options
|
||||
],
|
||||
label=pg.label,
|
||||
help_text=pg.help_text,
|
||||
help_text=conditional_escape(pg.help_text),
|
||||
initial=initial,
|
||||
widget=forms.RadioSelect,
|
||||
)
|
||||
|
||||
@@ -87,6 +87,7 @@ class RRuleForm(forms.Form):
|
||||
('1', pgettext_lazy('rrule', 'first')),
|
||||
('2', pgettext_lazy('rrule', 'second')),
|
||||
('3', pgettext_lazy('rrule', 'third')),
|
||||
('4', pgettext_lazy('rrule', 'fourth')),
|
||||
('-1', pgettext_lazy('rrule', 'last')),
|
||||
],
|
||||
required=False
|
||||
@@ -134,6 +135,7 @@ class RRuleForm(forms.Form):
|
||||
('1', pgettext_lazy('rrule', 'first')),
|
||||
('2', pgettext_lazy('rrule', 'second')),
|
||||
('3', pgettext_lazy('rrule', 'third')),
|
||||
('4', pgettext_lazy('rrule', 'fourth')),
|
||||
('-1', pgettext_lazy('rrule', 'last')),
|
||||
],
|
||||
required=False
|
||||
|
||||
@@ -435,10 +435,10 @@ class SubEventMetaValueForm(forms.ModelForm):
|
||||
if self.disabled:
|
||||
self.fields['value'].widget.attrs['readonly'] = 'readonly'
|
||||
|
||||
def clean_slug(self):
|
||||
def clean_value(self):
|
||||
if self.disabled:
|
||||
return self.instance.value if self.instance else None
|
||||
return self.cleaned_data['slug']
|
||||
return self.cleaned_data['value']
|
||||
|
||||
class Meta:
|
||||
model = SubEventMetaValue
|
||||
|
||||
@@ -44,6 +44,7 @@ from django.db.models import Count, F, Max
|
||||
from django.db.models.functions import Upper
|
||||
from django.forms.utils import ErrorDict
|
||||
from django.urls import reverse
|
||||
from django.utils.html import escape
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext_lazy as _, pgettext_lazy
|
||||
from django_scopes.forms import SafeModelChoiceField
|
||||
@@ -176,7 +177,7 @@ class VoucherForm(I18nModelForm):
|
||||
required=False,
|
||||
widget=forms.TextInput(attrs={'data-seat-guid-field': '1'}),
|
||||
initial=self.instance.seat.seat_guid if self.instance.seat else '',
|
||||
help_text=str(self.instance.seat) if self.instance.seat else '',
|
||||
help_text=escape(str(self.instance.seat) if self.instance.seat else ''),
|
||||
)
|
||||
|
||||
def parse_itemvar(self, data):
|
||||
|
||||
@@ -717,6 +717,10 @@ class CoreUserImpersonatedLogEntryType(UserImpersonatedLogEntryType):
|
||||
'pretix.organizer.export.schedule.failed': _('A scheduled export has failed: {reason}.'),
|
||||
'pretix.organizer.outgoingmails.retried': _('Failed emails have been scheduled to be retried.'),
|
||||
'pretix.organizer.outgoingmails.aborted': _('Queued emails have been aborted.'),
|
||||
'pretix.property.created': _('An organizer meta property has been created.'),
|
||||
'pretix.property.deleted': _('An organizer meta property has been deleted.'),
|
||||
'pretix.property.changed': _('An organizer meta property has been changed.'),
|
||||
'pretix.property.reordered': _('An organizer meta property has been reordered.'),
|
||||
'pretix.giftcards.acceptance.added': _('Gift card acceptance for another organizer has been added.'),
|
||||
'pretix.giftcards.acceptance.removed': _('Gift card acceptance for another organizer has been removed.'),
|
||||
'pretix.giftcards.acceptance.acceptor.invited': _('A new gift card acceptor has been invited.'),
|
||||
@@ -774,6 +778,7 @@ class CoreUserImpersonatedLogEntryType(UserImpersonatedLogEntryType):
|
||||
'pretix.user.settings.2fa.disabled': _('Two-factor authentication has been disabled.'),
|
||||
'pretix.user.settings.2fa.regenemergency': _('Your two-factor emergency codes have been regenerated.'),
|
||||
'pretix.user.settings.2fa.emergency': _('A two-factor emergency code has been generated.'),
|
||||
'pretix.user.settings.2fa.resetdrift': _('Drift and throttle values for two-factor devices have been reset.'),
|
||||
'pretix.user.settings.2fa.device.added': _('A new two-factor authentication device "{name}" has been added to '
|
||||
'your account.'),
|
||||
'pretix.user.settings.2fa.device.deleted': _('The two-factor authentication device "{name}" has been removed '
|
||||
|
||||
@@ -85,8 +85,8 @@ class PermissionMiddleware:
|
||||
"user.settings.2fa.enable",
|
||||
"user.settings.2fa.disable",
|
||||
"user.settings.2fa.regenemergency",
|
||||
"user.settings.2fa.confirm.totp",
|
||||
"user.settings.2fa.confirm.webauthn",
|
||||
"user.settings.2fa.confirm.otp_totp.totpdevice",
|
||||
"user.settings.2fa.confirm.pretixbase.webauthndevice",
|
||||
"user.settings.2fa.delete",
|
||||
"user.settings.2fa.leaveteams",
|
||||
"auth.logout",
|
||||
|
||||
@@ -45,8 +45,8 @@
|
||||
</p>
|
||||
|
||||
<div class="form-group buttons">
|
||||
<input type="submit" class="btn btn-large btn-default" value="Cancel"/>
|
||||
<input type="submit" class="btn btn-large btn-primary" name="allow" value="Authorize"/>
|
||||
<input type="submit" class="btn btn-large btn-default" value="{% trans "Cancel" %}"/>
|
||||
<input type="submit" class="btn btn-large btn-primary" name="allow" value="{% trans "Authorize" %}"/>
|
||||
</div>
|
||||
</form>
|
||||
{% else %}
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% load bootstrap3 %}
|
||||
{% load getitem %}
|
||||
{% block inside %}
|
||||
<h1>{% trans "Payment settings" %}</h1>
|
||||
<form action="" method="post" class="form-horizontal form-plugins">
|
||||
<form action="" method="post" class="form-horizontal">
|
||||
{% csrf_token %}
|
||||
<div class="tabbed-form">
|
||||
<fieldset>
|
||||
@@ -71,20 +72,44 @@
|
||||
{% bootstrap_form_errors form layout="control" %}
|
||||
{% bootstrap_field form.payment_term_mode layout="control" %}
|
||||
{% bootstrap_field form.payment_term_days layout="control" %}
|
||||
{% bootstrap_field form.payment_term_weekdays layout="control" %}
|
||||
{% bootstrap_field form.payment_term_minutes layout="control" %}
|
||||
{% bootstrap_field form.payment_term_weekdays layout="control" %}
|
||||
{% bootstrap_field form.payment_term_last layout="control" %}
|
||||
{% bootstrap_field form.payment_term_expire_automatically layout="control" %}
|
||||
{% trans "days" context "unit" as days %}
|
||||
{% bootstrap_field form.payment_term_expire_delay_days layout="control" addon_after=days %}
|
||||
{% bootstrap_field form.payment_term_accept_late layout="control" %}
|
||||
{% bootstrap_field form.payment_pending_hidden layout="control" %}
|
||||
|
||||
{% for c, fields in form.term_channel_fields.items %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">
|
||||
{% if "." in c.icon %}
|
||||
<img src="{% static c.icon %}" class="fa-like-image"
|
||||
data-toggle="tooltip" title="{{ c.type_instance.verbose_name }}">
|
||||
{% else %}
|
||||
<span class="fa fa-fw fa-{{ c.icon }} text-muted"
|
||||
data-toggle="tooltip" title="{{ c.type_instance.verbose_name }}"></span>
|
||||
{% endif %}
|
||||
{{ c.label }}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{% for f in fields %}
|
||||
{% bootstrap_field form|getitem:f layout="control" %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>{% trans "Advanced" %}</legend>
|
||||
{% bootstrap_form_errors form layout="control" %}
|
||||
{% bootstrap_field form.tax_rule_payment layout="control" %}
|
||||
{% bootstrap_field form.payment_explanation layout="control" %}
|
||||
{% bootstrap_field form.payment_choice_postpone_allowed_channels layout="control" %}
|
||||
</fieldset>
|
||||
</div>
|
||||
{% if "event.settings.payment:write" in request.eventpermset %}
|
||||
|
||||
@@ -275,6 +275,9 @@
|
||||
{% bootstrap_field sform.event_calendar_future_only layout="control" %}
|
||||
{% endif %}
|
||||
{% bootstrap_field sform.low_availability_percentage layout="control" addon_after="%" %}
|
||||
{% if sform.event_list_empty_text %}
|
||||
{% bootstrap_field sform.event_list_empty_text layout="control" %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
<h4>{% trans "Order details" %}</h4>
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
</div>
|
||||
<div class="slug-length alert alert-warning helper-display-none-soft">
|
||||
{% blocktrans trimmed %}
|
||||
We strongly recommend against using short forms of more then 16 characters.
|
||||
We strongly recommend against using short forms of more than 16 characters.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
{% bootstrap_field form.valid_datetime_max layout="control" %}
|
||||
</div>
|
||||
<div id="valid-string">
|
||||
{% bootstrap_field form.valid_string_length_min layout="control" %}
|
||||
{% bootstrap_field form.valid_string_length_max layout="control" %}
|
||||
</div>
|
||||
<div id="valid-file">
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<div>
|
||||
<div class="big-radio radio">
|
||||
<label>
|
||||
<input type="radio" required value="totp" name="{{ form.devicetype.html_name }}" {% if form.devicetype.value == "totp" %}checked{% endif %}>
|
||||
<input type="radio" required value="otp_totp.totpdevice" name="{{ form.devicetype.html_name }}" {% if form.devicetype.value == "otp_totp.totpdevice" %}checked{% endif %}>
|
||||
<strong>{% trans "Smartphone with Authenticator app" %}</strong><br>
|
||||
<div class="help-block">
|
||||
{% blocktrans trimmed %}
|
||||
@@ -26,7 +26,7 @@
|
||||
</div>
|
||||
<div class="big-radio radio">
|
||||
<label>
|
||||
<input type="radio" required value="webauthn" name="{{ form.devicetype.html_name }}" {% if form.devicetype.value == "webauthn" %}checked{% endif %}>
|
||||
<input type="radio" required value="pretixbase.webauthndevice" name="{{ form.devicetype.html_name }}" {% if form.devicetype.value == "pretixbase.webauthndevice" %}checked{% endif %}>
|
||||
<strong>{% trans "WebAuthn-compatible hardware token" %}</strong><br>
|
||||
<div class="help-block">
|
||||
{% blocktrans trimmed %}
|
||||
|
||||
@@ -116,14 +116,14 @@
|
||||
{% for d in devices %}
|
||||
<li class="list-group-item">
|
||||
<a class="btn btn-danger btn-xs pull-right flip"
|
||||
href="{% url "control:user.settings.2fa.delete" devicetype=d.devicetype device=d.pk %}">
|
||||
href="{% url "control:user.settings.2fa.delete" devicetype=d.model_label device=d.pk %}">
|
||||
Delete
|
||||
</a>
|
||||
{% if d.devicetype == "totp" %}
|
||||
{% if d.model_label == "otp_totp.totpdevice" %}
|
||||
<span class="fa fa-mobile"></span>
|
||||
{% elif d.devicetype == "webauthn" %}
|
||||
{% elif d.model_label == "pretixbase.webauthndevice" %}
|
||||
<span class="fa fa-usb"></span>
|
||||
{% elif d.devicetype == "u2f" %}
|
||||
{% elif d.model_label == "pretixbase.u2fdevice" %}
|
||||
<span class="fa fa-usb"></span>
|
||||
{% endif %}
|
||||
{{ d.name }}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{% extends "pretixcontrol/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load bootstrap3 %}
|
||||
{% load icon %}
|
||||
{% block title %}{% trans "User" %}{% endblock %}
|
||||
{% block content %}
|
||||
<h1>{% trans "User" %} {{ user.email }}</h1>
|
||||
@@ -59,8 +60,83 @@
|
||||
{% bootstrap_field form.is_verified layout='control' %}
|
||||
{% endif %}
|
||||
{% bootstrap_field form.last_login layout='control' %}
|
||||
{% bootstrap_field form.require_2fa layout='control' %}
|
||||
{% bootstrap_field form.needs_password_change layout='control' %}
|
||||
{% bootstrap_field form.require_2fa layout='control' %}
|
||||
<div class="form-group">
|
||||
<div class="col-md-9 col-md-offset-3">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<button class="btn btn-default btn-xs pull-right" type="submit" form="resetdriftthrottle">
|
||||
{% trans "Reset drift and throttle" %}
|
||||
</button>
|
||||
<h3 class="panel-title">
|
||||
{% trans "Available two-factor authentication methods" %}
|
||||
</h3>
|
||||
</div>
|
||||
<table class="panel-body table table-hover">
|
||||
{% for d in devices %}
|
||||
<tr>
|
||||
<td>
|
||||
{% if d.model_label == 'otp_totp.totpdevice' %}
|
||||
TOTP
|
||||
{% elif d.model_label == 'pretixbase.u2fdevice' %}
|
||||
U2F
|
||||
{% elif d.model_label == 'pretixbase.webauthndevice' %}
|
||||
WebAuthn
|
||||
{% elif d.model_label == 'otp_static.staticdevice' %}
|
||||
{% trans "Emergency tokens" %}
|
||||
{% endif %}
|
||||
{% if d.confirmed %}
|
||||
{% icon "check" %}
|
||||
{% else %}
|
||||
{% icon "warning" %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ d.name }}
|
||||
</td>
|
||||
<td>
|
||||
{% if d.throttling_failure_timestamp %}
|
||||
{% blocktrans trimmed with date=d.throttling_failure_timestamp|date:"SHORT_DATETIME_FORMAT" count cnt=d.throttling_failure_count %}
|
||||
1 failed attempt since {{ date }}
|
||||
{% plural %}
|
||||
{{ cnt }} failed attempts since {{ date }}
|
||||
{% endblocktrans %}
|
||||
<br>
|
||||
{% endif %}
|
||||
{% if d.throttling_enabled and not d.verify_is_allowed.0 %}
|
||||
<strong>
|
||||
{% blocktrans trimmed with date=d.verify_is_allowed.1.locked_until|date:"SHORT_DATETIME_FORMAT" %}
|
||||
Currently locked until {{ date }}
|
||||
{% endblocktrans %}
|
||||
</strong>
|
||||
<br>
|
||||
{% endif %}
|
||||
{% if d.model_label == 'otp_totp.totpdevice' %}
|
||||
<small>
|
||||
<code>step = {{ d.step }},
|
||||
t0 = {{ d.t0 }},
|
||||
digits = {{ d.digits }},
|
||||
tolerance = {{ d.tolerance }},
|
||||
drift = {{ d.drift }},
|
||||
last_t = {{ d.last_t }}</code>
|
||||
</small>
|
||||
{% elif d.model_label == 'pretixbase.u2fdevice' %}
|
||||
<small>
|
||||
<code>sign_count = {{ d.sign_count }}</code>
|
||||
</small>
|
||||
{% elif d.model_label == 'otp_static.staticdevice' %}
|
||||
<small>
|
||||
<code>token_count = {{ d.token_set.count }}</code>
|
||||
</small>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>{% trans "Team memberships" %}</legend>
|
||||
@@ -102,4 +178,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form action="{% url "control:users.resetdriftthrottle" id=user.pk %}" id="resetdriftthrottle" method="post">
|
||||
{% csrf_token %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
@@ -78,6 +78,7 @@ urlpatterns = [
|
||||
re_path(r'^users/(?P<id>\d+)/impersonate$', users.UserImpersonateView.as_view(), name='users.impersonate'),
|
||||
re_path(r'^users/(?P<id>\d+)/anonymize$', users.UserAnonymizeView.as_view(), name='users.anonymize'),
|
||||
re_path(r'^users/(?P<id>\d+)/emergencytoken$', users.UserEmergencyTokenView.as_view(), name='users.emergencytoken'),
|
||||
re_path(r'^users/(?P<id>\d+)/resetdriftthrottle$', users.Reset2FADriftThrottleView.as_view(), name='users.resetdriftthrottle'),
|
||||
re_path(r'^pdf/editor/webfonts.css', pdf.FontsCSSView.as_view(), name='pdf.css'),
|
||||
re_path(r'^settings/?$', user.UserSettings.as_view(), name='user.settings'),
|
||||
re_path(r'^settings/history/$', user.UserHistoryView.as_view(), name='user.settings.history'),
|
||||
@@ -106,9 +107,9 @@ urlpatterns = [
|
||||
re_path(r'^settings/2fa/regenemergency', user.User2FARegenerateEmergencyView.as_view(),
|
||||
name='user.settings.2fa.regenemergency'),
|
||||
re_path(r'^settings/2fa/totp/(?P<device>[0-9]+)/confirm', user.User2FADeviceConfirmTOTPView.as_view(),
|
||||
name='user.settings.2fa.confirm.totp'),
|
||||
name='user.settings.2fa.confirm.otp_totp.totpdevice'),
|
||||
re_path(r'^settings/2fa/webauthn/(?P<device>[0-9]+)/confirm', user.User2FADeviceConfirmWebAuthnView.as_view(),
|
||||
name='user.settings.2fa.confirm.webauthn'),
|
||||
name='user.settings.2fa.confirm.pretixbase.webauthndevice'),
|
||||
re_path(r'^settings/2fa/(?P<devicetype>[^/]+)/(?P<device>[0-9]+)/delete', user.User2FADeviceDeleteView.as_view(),
|
||||
name='user.settings.2fa.delete'),
|
||||
re_path(r'^settings/email/confirm$', user.UserEmailConfirmView.as_view(), name='user.settings.email.confirm'),
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
import base64
|
||||
import json
|
||||
import logging
|
||||
import math
|
||||
import time
|
||||
from urllib.parse import quote, urljoin, urlparse
|
||||
|
||||
@@ -50,11 +51,12 @@ from django.shortcuts import redirect, render
|
||||
from django.urls import reverse
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.http import url_has_allowed_host_and_scheme
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext_lazy as _, ngettext
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.views.decorators.http import require_http_methods
|
||||
from django.views.generic import TemplateView
|
||||
from django_otp import match_token
|
||||
from django_otp import devices_for_user
|
||||
from django_otp.plugins.otp_static.models import StaticDevice
|
||||
from webauthn.helpers import generate_challenge
|
||||
|
||||
@@ -64,6 +66,7 @@ from pretix.base.forms.auth import (
|
||||
)
|
||||
from pretix.base.metrics import pretix_failed_logins, pretix_successful_logins
|
||||
from pretix.base.models import TeamInvite, U2FDevice, User, WebAuthnDevice
|
||||
from pretix.helpers import OF_SELF
|
||||
from pretix.helpers.http import get_client_ip, redirect_to_url
|
||||
from pretix.helpers.ratelimit import rate_limit, rate_limit_reset
|
||||
from pretix.helpers.security import handle_login_source, session_login
|
||||
@@ -395,15 +398,17 @@ class Recover(TemplateView):
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
if self.form.is_valid():
|
||||
try:
|
||||
user = User.objects.get(id=self.request.GET.get('id'), auth_backend='native')
|
||||
except User.DoesNotExist:
|
||||
return self.invalid('unknownuser')
|
||||
if not default_token_generator.check_token(user, self.request.GET.get('token')):
|
||||
return self.invalid('invalid')
|
||||
user.set_password(self.form.cleaned_data['password'])
|
||||
user.needs_password_change = False
|
||||
user.save()
|
||||
with transaction.atomic():
|
||||
# Check token in transaction to prevent race condition
|
||||
try:
|
||||
user = User.objects.select_for_update(of=OF_SELF).get(id=self.request.GET.get('id'), auth_backend='native')
|
||||
except User.DoesNotExist:
|
||||
return self.invalid('unknownuser')
|
||||
if not default_token_generator.check_token(user, self.request.GET.get('token')):
|
||||
return self.invalid('invalid')
|
||||
user.set_password(self.form.cleaned_data['password'])
|
||||
user.needs_password_change = False
|
||||
user.save()
|
||||
messages.success(request, _('You can now login using your new password.'))
|
||||
user.log_action('pretix.control.auth.user.forgot_password.recovered')
|
||||
|
||||
@@ -460,6 +465,7 @@ class Login2FAView(TemplateView):
|
||||
token = request.POST.get('token', '').strip().replace(' ', '')
|
||||
|
||||
valid = False
|
||||
retry_after = None
|
||||
if 'webauthn_challenge' in self.request.session and token.startswith('{'):
|
||||
challenge = self.request.session['webauthn_challenge']
|
||||
|
||||
@@ -515,12 +521,28 @@ class Login2FAView(TemplateView):
|
||||
valid = True
|
||||
break
|
||||
else:
|
||||
valid = match_token(self.user, token)
|
||||
if isinstance(valid, StaticDevice):
|
||||
with transaction.atomic():
|
||||
for device in devices_for_user(self.user, for_verify=True):
|
||||
if isinstance(device, StaticDevice) and len(token) < 12:
|
||||
# If we enter a wrong TOTP token (which is 6 characters), do not even try if it is a valid
|
||||
# emergency token, which will only "lock up" the StaticDevice due to the throttling plugin
|
||||
# and just locks people out without security gain.
|
||||
continue
|
||||
if device.verify_token(token):
|
||||
valid = True
|
||||
break
|
||||
elif hasattr(device, 'verify_is_allowed'):
|
||||
verify_allowed, reason_dict = device.verify_is_allowed()
|
||||
if not verify_allowed:
|
||||
if not retry_after or reason_dict['locked_until'] > retry_after:
|
||||
retry_after = reason_dict['locked_until']
|
||||
else:
|
||||
device = None
|
||||
|
||||
if isinstance(device, StaticDevice):
|
||||
self.user.send_security_notice([
|
||||
_("A recovery code for two-factor authentification was used to log in.")
|
||||
])
|
||||
|
||||
if valid:
|
||||
logger.info(f"Backend login successful for user {self.user.pk} with 2FA.")
|
||||
pretix_successful_logins.inc(1)
|
||||
@@ -533,7 +555,23 @@ class Login2FAView(TemplateView):
|
||||
return redirect('control:index')
|
||||
else:
|
||||
pretix_failed_logins.inc(1, reason="2fa")
|
||||
messages.error(request, _('Invalid code, please try again.'))
|
||||
msg = _('Invalid code, please try again.')
|
||||
if retry_after:
|
||||
seconds = (retry_after - now()).total_seconds()
|
||||
minutes = seconds / 60
|
||||
if minutes >= 1:
|
||||
msg = ngettext(
|
||||
'Invalid code. Please try again after waiting {value} minute.',
|
||||
'Invalid code. Please try again after waiting {value} minutes.',
|
||||
minutes,
|
||||
).format(value=math.ceil(minutes))
|
||||
elif seconds >= 1:
|
||||
msg = ngettext(
|
||||
'Invalid code. Please try again after waiting {value} second.',
|
||||
'Invalid code. Please try again after waiting {value} seconds.',
|
||||
seconds,
|
||||
).format(value=math.ceil(seconds))
|
||||
messages.error(request, msg)
|
||||
return redirect('control:auth.login.2fa')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
||||
@@ -402,9 +402,12 @@ def event_index_widgets_lazy(request, organizer, event):
|
||||
except SubEvent.DoesNotExist:
|
||||
pass
|
||||
|
||||
can_view_orders = request.user.has_event_permission(request.organizer, request.event, 'event.orders:read',
|
||||
request=request)
|
||||
widgets = []
|
||||
for r, result in event_dashboard_widgets.send(sender=request.event, subevent=subevent, lazy=False):
|
||||
widgets.extend(result)
|
||||
if can_view_orders:
|
||||
for r, result in event_dashboard_widgets.send(sender=request.event, subevent=subevent, lazy=False):
|
||||
widgets.extend(result)
|
||||
|
||||
return build_json_response(widgets)
|
||||
|
||||
|
||||
@@ -163,6 +163,9 @@ class DiscountCreate(EventPermissionRequiredMixin, CreateView):
|
||||
i = modelcopy(self.copy_from)
|
||||
i.pk = None
|
||||
kwargs['instance'] = i
|
||||
kwargs["initial"]["limit_sales_channels"] = self.copy_from.limit_sales_channels.all()
|
||||
kwargs["initial"]["condition_limit_products"] = self.copy_from.condition_limit_products.all()
|
||||
kwargs["initial"]["benefit_limit_products"] = self.copy_from.benefit_limit_products.all()
|
||||
else:
|
||||
kwargs['instance'] = Discount(event=self.request.event)
|
||||
|
||||
|
||||
@@ -1177,6 +1177,8 @@ class OrderRefundView(OrderView):
|
||||
manual_value = formats.sanitize_separators(manual_value)
|
||||
try:
|
||||
manual_value = Decimal(manual_value)
|
||||
if manual_value < Decimal("0.00"):
|
||||
raise TypeError("Please do not use negative numbers")
|
||||
except (DecimalException, TypeError):
|
||||
messages.error(self.request, _('You entered an invalid number.'))
|
||||
is_valid = False
|
||||
@@ -1206,6 +1208,8 @@ class OrderRefundView(OrderView):
|
||||
giftcard_value = formats.sanitize_separators(giftcard_value)
|
||||
try:
|
||||
giftcard_value = Decimal(giftcard_value)
|
||||
if giftcard_value < Decimal("0.00"):
|
||||
raise TypeError("Please do not use negative numbers")
|
||||
except (DecimalException, TypeError):
|
||||
messages.error(self.request, _('You entered an invalid number.'))
|
||||
is_valid = False
|
||||
@@ -1255,6 +1259,8 @@ class OrderRefundView(OrderView):
|
||||
offsetting_value = formats.sanitize_separators(offsetting_value)
|
||||
try:
|
||||
offsetting_value = Decimal(offsetting_value)
|
||||
if offsetting_value < Decimal("0.00"):
|
||||
raise TypeError("Please do not use negative numbers")
|
||||
except (DecimalException, TypeError):
|
||||
messages.error(self.request, _('You entered an invalid number.'))
|
||||
is_valid = False
|
||||
@@ -1271,6 +1277,9 @@ class OrderRefundView(OrderView):
|
||||
if offset_order.event.currency != self.request.event.currency:
|
||||
messages.error(self.request, _('You entered an order in an event with a different currency.'))
|
||||
is_valid = False
|
||||
if not self.request.user.has_event_permission(self.request.organizer, offset_order.event, 'event.orders:write', request=self.request):
|
||||
messages.error(self.request, _('You entered an order in an event that you do not have access to.'))
|
||||
is_valid = False
|
||||
refunds.append(OrderRefund(
|
||||
order=order,
|
||||
payment=None,
|
||||
@@ -1286,10 +1295,13 @@ class OrderRefundView(OrderView):
|
||||
))
|
||||
|
||||
for identifier, prov in self.request.event.get_payment_providers().items():
|
||||
# prof = process form, not a typo for prov(ider)
|
||||
prof_value = self.request.POST.get(f'newrefund-{identifier}', '0') or '0'
|
||||
prof_value = formats.sanitize_separators(prof_value)
|
||||
try:
|
||||
prof_value = Decimal(prof_value)
|
||||
if prof_value < Decimal("0.00"):
|
||||
raise TypeError("Please do not use negative numbers")
|
||||
except (DecimalException, TypeError):
|
||||
messages.error(self.request, _('You entered an invalid number.'))
|
||||
is_valid = False
|
||||
@@ -1313,6 +1325,8 @@ class OrderRefundView(OrderView):
|
||||
value = formats.sanitize_separators(value)
|
||||
try:
|
||||
value = Decimal(value)
|
||||
if value < Decimal("0.00"):
|
||||
raise TypeError("Please do not use negative numbers")
|
||||
except (DecimalException, TypeError):
|
||||
messages.error(self.request, _('You entered an invalid number.'))
|
||||
is_valid = False
|
||||
@@ -1342,7 +1356,12 @@ class OrderRefundView(OrderView):
|
||||
))
|
||||
|
||||
any_success = False
|
||||
if refund_selected == full_refund and is_valid:
|
||||
if refund_selected != full_refund:
|
||||
messages.error(self.request, _('The refunds you selected do not match the selected total refund '
|
||||
'amount.'))
|
||||
is_valid = False
|
||||
|
||||
if is_valid:
|
||||
for r in refunds:
|
||||
r.save()
|
||||
order.log_action('pretix.event.order.refund.created', {
|
||||
@@ -1414,9 +1433,6 @@ class OrderRefundView(OrderView):
|
||||
)
|
||||
}))
|
||||
return redirect(self.get_order_url())
|
||||
else:
|
||||
messages.error(self.request, _('The refunds you selected do not match the selected total refund '
|
||||
'amount.'))
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
if self.start_form.is_valid():
|
||||
@@ -1646,7 +1662,8 @@ class OrderCheckVATID(OrderView):
|
||||
return redirect(self.get_order_url())
|
||||
|
||||
try:
|
||||
normalized_id = validate_vat_id(ia.vat_id, str(ia.country))
|
||||
requester_id = self.request.event.settings.invoice_address_from_vat_id
|
||||
normalized_id = validate_vat_id(ia.vat_id, str(ia.country), requester_id)
|
||||
with transaction.atomic():
|
||||
ia.vat_id_validated = True
|
||||
ia.vat_id = normalized_id
|
||||
@@ -2008,7 +2025,10 @@ class OrderChange(OrderView):
|
||||
'issued_gift_cards',
|
||||
'addons',
|
||||
).annotate(
|
||||
_seat_allowed=Exists(SeatCategoryMapping.objects.filter(subevent=OuterRef("subevent"), product=OuterRef("item")))
|
||||
_seat_allowed=Exists(SeatCategoryMapping.objects.filter(
|
||||
subevent=OuterRef("subevent") if self.request.event.has_subevents else None,
|
||||
product=OuterRef("item"))
|
||||
)
|
||||
))
|
||||
for p in positions:
|
||||
p.form = OrderPositionChangeForm(
|
||||
|
||||
@@ -778,9 +778,9 @@ class OrganizerPluginEvents(OrganizerDetailViewMixin, OrganizerPermissionRequire
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
kwargs["events"] = self.request.user.get_events_with_permission(
|
||||
"event.settings.general:write", request=self.request
|
||||
).filter(organizer=self.request.organizer)
|
||||
# Assumption: Who has access to modify organizer settings may see all events and disable/enable plugins
|
||||
# for them. Otherwise, inconsistent situations occur.
|
||||
kwargs["events"] = self.request.organizer.events.all()
|
||||
kwargs["initial"] = {
|
||||
"events": self.request.organizer.events.filter(plugins__regex='(^|,)' + self.plugin.module + '(,|$)')
|
||||
}
|
||||
@@ -2202,7 +2202,7 @@ class ExportView(OrganizerPermissionRequiredMixin, ExportMixin, ListView):
|
||||
owner=self.request.user,
|
||||
timezone=str(get_current_timezone()),
|
||||
)
|
||||
if not self.scheduled:
|
||||
if not self.scheduled and not self.scheduled_copy_from:
|
||||
initial = {
|
||||
"mail_subject": gettext("Export: {title}").format(title=self.exporter.verbose_name),
|
||||
"mail_template": gettext(
|
||||
|
||||
@@ -27,6 +27,7 @@ from decimal import Decimal
|
||||
from io import BytesIO
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.core.files import File
|
||||
from django.core.files.base import ContentFile
|
||||
from django.core.files.storage import default_storage
|
||||
@@ -193,6 +194,7 @@ class BaseEditorView(EventPermissionRequiredMixin, TemplateView):
|
||||
c.expires = now() + timedelta(days=7)
|
||||
c.date = now()
|
||||
c.filename = 'background_preview.pdf'
|
||||
c.bind_to_session(request, "ticketoutput-pdf-background")
|
||||
c.type = 'application/pdf'
|
||||
c.save()
|
||||
c.file.save('empty.pdf', ContentFile(buffer.read()))
|
||||
@@ -218,6 +220,7 @@ class BaseEditorView(EventPermissionRequiredMixin, TemplateView):
|
||||
c.expires = now() + timedelta(days=7)
|
||||
c.date = now()
|
||||
c.filename = 'background_preview.pdf'
|
||||
c.bind_to_session(request, "ticketoutput-pdf-background")
|
||||
c.type = 'application/pdf'
|
||||
c.file = fileobj
|
||||
c.save()
|
||||
@@ -303,5 +306,7 @@ class FontsCSSView(TemplateView):
|
||||
class PdfView(TemplateView):
|
||||
def get(self, request, *args, **kwargs):
|
||||
cf = get_object_or_404(CachedFile, id=kwargs.get("filename"), filename="background_preview.pdf")
|
||||
if not cf.allowed_for_session(request, "ticketoutput-pdf-background"):
|
||||
raise PermissionDenied()
|
||||
resp = FileResponse(cf.file, filename=cf.filename, content_type='application/pdf')
|
||||
return resp
|
||||
|
||||
@@ -1276,6 +1276,11 @@ class SubEventBulkEdit(SubEventQueryMixin, EventPermissionRequiredMixin, FormVie
|
||||
self._default_meta = self.request.event.meta_data
|
||||
|
||||
for p in self.request.organizer.meta_properties.all():
|
||||
if p.protected and not self.request.user.has_organizer_permission(
|
||||
self.request.organizer, 'organizer.settings.general:write', request=self.request
|
||||
):
|
||||
continue
|
||||
|
||||
inst = SubEventMetaValue(property=p)
|
||||
if len(matches[p.id]) == 1 and matches[p.id][0]['c'] == total:
|
||||
inst.value = matches[p.id][0]['value']
|
||||
|
||||
@@ -59,6 +59,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
from django.views import View
|
||||
from django.views.decorators.cache import never_cache
|
||||
from django.views.generic import FormView, ListView, TemplateView, UpdateView
|
||||
from django_otp import devices_for_user
|
||||
from django_otp.plugins.otp_static.models import StaticDevice
|
||||
from django_otp.plugins.otp_totp.models import TOTPDevice
|
||||
from django_scopes import scopes_disabled
|
||||
@@ -85,7 +86,6 @@ from pretix.helpers.ratelimit import rate_limit, rate_limit_reset
|
||||
from pretix.helpers.security import session_reauth
|
||||
from pretix.helpers.u2f import websafe_encode
|
||||
|
||||
REAL_DEVICE_TYPES = (TOTPDevice, WebAuthnDevice, U2FDevice)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -313,17 +313,7 @@ class User2FAMainView(RecentAuthenticationRequiredMixin, TemplateView):
|
||||
except StaticDevice.DoesNotExist:
|
||||
ctx['static_tokens_device'] = None
|
||||
|
||||
ctx['devices'] = []
|
||||
for dt in REAL_DEVICE_TYPES:
|
||||
objs = list(dt.objects.filter(user=self.request.user, confirmed=True))
|
||||
for obj in objs:
|
||||
if dt == TOTPDevice:
|
||||
obj.devicetype = 'totp'
|
||||
elif dt == U2FDevice:
|
||||
obj.devicetype = 'u2f'
|
||||
elif dt == WebAuthnDevice:
|
||||
obj.devicetype = 'webauthn'
|
||||
ctx['devices'] += objs
|
||||
ctx['devices'] = [d for d in devices_for_user(self.request.user) if not isinstance(d, StaticDevice)]
|
||||
|
||||
ctx['obligatory'] = None
|
||||
if settings.PRETIX_OBLIGATORY_2FA is True:
|
||||
@@ -342,9 +332,9 @@ class User2FADeviceAddView(RecentAuthenticationRequiredMixin, FormView):
|
||||
template_name = 'pretixcontrol/user/2fa_add.html'
|
||||
|
||||
def form_valid(self, form):
|
||||
if form.cleaned_data['devicetype'] == 'totp':
|
||||
if form.cleaned_data['devicetype'] == 'otp_totp.totpdevice':
|
||||
dev = TOTPDevice.objects.create(user=self.request.user, confirmed=False, name=form.cleaned_data['name'])
|
||||
elif form.cleaned_data['devicetype'] == 'webauthn':
|
||||
elif form.cleaned_data['devicetype'] == 'pretixbase.webauthndevice':
|
||||
if not self.request.is_secure():
|
||||
messages.error(self.request,
|
||||
_('Security devices are only available if pretix is served via HTTPS.'))
|
||||
@@ -364,11 +354,11 @@ class User2FADeviceDeleteView(RecentAuthenticationRequiredMixin, TemplateView):
|
||||
|
||||
@cached_property
|
||||
def device(self):
|
||||
if self.kwargs['devicetype'] == 'totp':
|
||||
if self.kwargs['devicetype'] == 'otp_totp.totpdevice':
|
||||
return get_object_or_404(TOTPDevice, user=self.request.user, pk=self.kwargs['device'], confirmed=True)
|
||||
elif self.kwargs['devicetype'] == 'webauthn':
|
||||
elif self.kwargs['devicetype'] == 'pretixbase.webauthndevice':
|
||||
return get_object_or_404(WebAuthnDevice, user=self.request.user, pk=self.kwargs['device'], confirmed=True)
|
||||
elif self.kwargs['devicetype'] == 'u2f':
|
||||
elif self.kwargs['devicetype'] == 'pretixbase.u2fdevice':
|
||||
return get_object_or_404(U2FDevice, user=self.request.user, pk=self.kwargs['device'], confirmed=True)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
@@ -386,7 +376,7 @@ class User2FADeviceDeleteView(RecentAuthenticationRequiredMixin, TemplateView):
|
||||
msgs = [
|
||||
_('A two-factor authentication device has been removed from your account.')
|
||||
]
|
||||
if not any(dt.objects.filter(user=self.request.user, confirmed=True) for dt in REAL_DEVICE_TYPES):
|
||||
if not any(d.confirmed for d in devices_for_user(self.request.user) if not isinstance(d, StaticDevice)):
|
||||
self.request.user.require_2fa = False
|
||||
self.request.user.save()
|
||||
self.request.user.log_action('pretix.user.settings.2fa.disabled', user=self.request.user)
|
||||
@@ -461,7 +451,7 @@ class User2FADeviceConfirmWebAuthnView(RecentAuthenticationRequiredMixin, Templa
|
||||
).first()
|
||||
if credential_id_exists:
|
||||
messages.error(request, _('This security device is already registered.'))
|
||||
return redirect(reverse('control:user.settings.2fa.confirm.webauthn', kwargs={
|
||||
return redirect(reverse('control:user.settings.2fa.confirm.pretixbase.webauthndevice', kwargs={
|
||||
'device': self.device.pk
|
||||
}))
|
||||
|
||||
@@ -475,7 +465,7 @@ class User2FADeviceConfirmWebAuthnView(RecentAuthenticationRequiredMixin, Templa
|
||||
self.device.save()
|
||||
self.request.user.log_action('pretix.user.settings.2fa.device.added', user=self.request.user, data={
|
||||
'id': self.device.pk,
|
||||
'devicetype': 'u2f',
|
||||
'devicetype': 'pretixbase.webauthndevice',
|
||||
'name': self.device.name,
|
||||
})
|
||||
notices = [
|
||||
@@ -503,7 +493,7 @@ class User2FADeviceConfirmWebAuthnView(RecentAuthenticationRequiredMixin, Templa
|
||||
except Exception:
|
||||
messages.error(request, _('The registration could not be completed. Please try again.'))
|
||||
logger.exception('WebAuthn registration failed')
|
||||
return redirect(reverse('control:user.settings.2fa.confirm.webauthn', kwargs={
|
||||
return redirect(reverse('control:user.settings.2fa.confirm.pretixbase.webauthndevice', kwargs={
|
||||
'device': self.device.pk
|
||||
}))
|
||||
|
||||
@@ -537,7 +527,7 @@ class User2FADeviceConfirmTOTPView(RecentAuthenticationRequiredMixin, TemplateVi
|
||||
self.request.user.log_action('pretix.user.settings.2fa.device.added', user=self.request.user, data={
|
||||
'id': self.device.pk,
|
||||
'name': self.device.name,
|
||||
'devicetype': 'totp'
|
||||
'devicetype': 'otp_totp.totpdevice'
|
||||
})
|
||||
notices = [
|
||||
_('A new two-factor authentication device has been added to your account.')
|
||||
@@ -563,7 +553,7 @@ class User2FADeviceConfirmTOTPView(RecentAuthenticationRequiredMixin, TemplateVi
|
||||
else:
|
||||
messages.error(request, _('The code you entered was not valid. If this problem persists, please check '
|
||||
'that the date and time of your phone are configured correctly.'))
|
||||
return redirect(reverse('control:user.settings.2fa.confirm.totp', kwargs={
|
||||
return redirect(reverse('control:user.settings.2fa.confirm.otp_totp.totpdevice', kwargs={
|
||||
'device': self.device.pk
|
||||
}))
|
||||
|
||||
@@ -594,7 +584,7 @@ class User2FAEnableView(RecentAuthenticationRequiredMixin, TemplateView):
|
||||
template_name = 'pretixcontrol/user/2fa_enable.html'
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if not any(dt.objects.filter(user=self.request.user, confirmed=True) for dt in REAL_DEVICE_TYPES):
|
||||
if not any(d.confirmed for d in devices_for_user(self.request.user) if not isinstance(d, StaticDevice)):
|
||||
messages.error(request, _('Please configure at least one device before enabling two-factor '
|
||||
'authentication.'))
|
||||
return redirect(reverse('control:user.settings.2fa'))
|
||||
@@ -956,7 +946,7 @@ class UserEmailConfirmView(FormView):
|
||||
|
||||
@transaction.atomic()
|
||||
def form_valid(self, form):
|
||||
reason = self.request.GET['reason']
|
||||
reason = self.request.GET.get('reason')
|
||||
if reason not in ('email_change', 'email_verify'):
|
||||
raise PermissionDenied
|
||||
try:
|
||||
|
||||
@@ -40,6 +40,7 @@ from django.utils.functional import cached_property
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views import View
|
||||
from django.views.generic import ListView, TemplateView
|
||||
from django_otp import devices_for_user
|
||||
from django_otp.plugins.otp_static.models import StaticDevice
|
||||
from hijack import signals
|
||||
|
||||
@@ -107,6 +108,9 @@ class UserEditView(AdministratorPermissionRequiredMixin, RecentAuthenticationReq
|
||||
ctx['backend'] = (
|
||||
b[self.object.auth_backend].verbose_name if self.object.auth_backend in b else self.object.auth_backend
|
||||
)
|
||||
|
||||
ctx['devices'] = devices_for_user(self.object)
|
||||
|
||||
return ctx
|
||||
|
||||
def get_success_url(self):
|
||||
@@ -183,6 +187,25 @@ class UserEmergencyTokenView(AdministratorPermissionRequiredMixin, RecentAuthent
|
||||
return reverse('control:users.edit', kwargs=self.kwargs)
|
||||
|
||||
|
||||
class Reset2FADriftThrottleView(AdministratorPermissionRequiredMixin, RecentAuthenticationRequiredMixin, View):
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
return redirect(reverse('control:users.edit', kwargs=self.kwargs))
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.object = get_object_or_404(User, pk=self.kwargs.get("id"))
|
||||
self.object.totpdevice_set.update(drift=0, throttling_failure_timestamp=None, throttling_failure_count=0)
|
||||
self.object.staticdevice_set.update(throttling_failure_timestamp=None, throttling_failure_count=0)
|
||||
self.object.log_action('pretix.user.settings.2fa.resetdrift', user=self.request.user)
|
||||
messages.success(request, _(
|
||||
'The drift values for TOTP devices have been reset.'
|
||||
))
|
||||
return redirect(self.get_success_url())
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('control:users.edit', kwargs=self.kwargs)
|
||||
|
||||
|
||||
class UserAnonymizeView(AdministratorPermissionRequiredMixin, RecentAuthenticationRequiredMixin, TemplateView):
|
||||
template_name = "pretixcontrol/users/anonymize.html"
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ class VoucherList(VoucherQueryMixin, PaginationMixin, EventPermissionRequiredMix
|
||||
headers = [
|
||||
_('Voucher code'), _('Valid until'), _('Product'), _('Reserve quota'), _('Bypass quota'),
|
||||
_('Price effect'), _('Value'), _('Tag'), _('Redeemed'), _('Maximum usages'), _('Seat'),
|
||||
_('Comment')
|
||||
_('Comment'), _('Budget'), _('Budget used')
|
||||
]
|
||||
writer.writerow(headers)
|
||||
|
||||
@@ -170,7 +170,9 @@ class VoucherList(VoucherQueryMixin, PaginationMixin, EventPermissionRequiredMix
|
||||
str(v.redeemed),
|
||||
str(v.max_usages),
|
||||
str(v.seat) if v.seat else "",
|
||||
str(v.comment) if v.comment else ""
|
||||
str(v.comment) if v.comment else "",
|
||||
str(v.budget) if v.budget is not None else "",
|
||||
str(v.budget_used) if v.budget is not None else "",
|
||||
]
|
||||
writer.writerow(row)
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ from http import cookies
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import SuspiciousFileOperation
|
||||
from django.middleware.csrf import CsrfViewMiddleware as BaseCsrfMiddleware
|
||||
from PIL import Image
|
||||
from requests.adapters import HTTPAdapter
|
||||
from urllib3.connection import HTTPConnection, HTTPSConnection
|
||||
@@ -42,6 +43,7 @@ from urllib3.util.timeout import _DEFAULT_TIMEOUT
|
||||
|
||||
from pretix.helpers.reportlab import ThumbnailingImageReader
|
||||
from pretix.helpers.ssrf import should_block_access
|
||||
from pretix.multidomain.middlewares import CsrfViewMiddleware
|
||||
|
||||
|
||||
def monkeypatch_vobject_performance():
|
||||
@@ -242,6 +244,20 @@ def monkeypatch_reportlab_imagereader():
|
||||
utils.ImageReader.__init__ = new_init
|
||||
|
||||
|
||||
def monkeypatch_csrf_middleware():
|
||||
# Some views from django or plugins use the middleware or the derived decorators directly,
|
||||
# so we have to also patch the stock middleware
|
||||
BaseCsrfMiddleware._get_secret = CsrfViewMiddleware._get_secret
|
||||
BaseCsrfMiddleware._set_csrf_cookie = CsrfViewMiddleware._set_csrf_cookie
|
||||
|
||||
|
||||
def monkeypatch_json_constants():
|
||||
from json.decoder import _CONSTANTS # noqa
|
||||
del _CONSTANTS['-Infinity']
|
||||
del _CONSTANTS['Infinity']
|
||||
del _CONSTANTS['NaN']
|
||||
|
||||
|
||||
def monkeypatch_all_at_ready():
|
||||
monkeypatch_vobject_performance()
|
||||
monkeypatch_pillow_safer()
|
||||
@@ -249,3 +265,5 @@ def monkeypatch_all_at_ready():
|
||||
monkeypatch_urllib3_ssrf_protection()
|
||||
monkeypatch_cookie_morsel()
|
||||
monkeypatch_reportlab_imagereader()
|
||||
monkeypatch_csrf_middleware()
|
||||
monkeypatch_json_constants()
|
||||
|
||||
@@ -29,7 +29,7 @@ from django.urls import reverse
|
||||
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 ',
|
||||
'Replace this usage with mainreverse_absolute.',
|
||||
DeprecationWarning
|
||||
)
|
||||
return mainreverse_absolute(urlname, args, kwargs)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-07-07 09:33+0000\n"
|
||||
"POT-Creation-Date: 2026-09-30 07:26+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: Automatically generated\n"
|
||||
"Language-Team: none\n"
|
||||
@@ -765,194 +765,163 @@ msgstr ""
|
||||
msgid "Google Pay"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Quantity"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Decrease quantity"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Increase quantity"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Filter events by"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Filter"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Price"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "Original price: %s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "New price: %s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Select"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "Select %s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "Select variant %s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Sold out"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Buy"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Register"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Reserved"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "FREE"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "from %(currency)s %(price)s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "Image of %s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "incl. %(rate)s% %(taxname)s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "plus %(rate)s% %(taxname)s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "incl. taxes"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "plus taxes"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "currently available: %s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Only available with a voucher"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Not yet available"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Not available anymore"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Currently not available"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "minimum amount to order: %s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Close ticket shop"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "The ticket shop could not be loaded."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
@@ -960,25 +929,21 @@ msgid ""
|
||||
"in a new tab to continue."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Open ticket shop"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Checkout"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "The cart could not be created. Please try again later"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
@@ -986,13 +951,11 @@ msgid ""
|
||||
"this ticket shop. Please click \"Continue\" to retry in a new tab."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Waiting list"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
@@ -1000,115 +963,96 @@ msgid ""
|
||||
"products, they will be added to your existing cart."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Resume checkout"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Redeem a voucher"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Redeem"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Voucher code"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Close checkout"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "You cannot cancel this operation. Please wait for loading to finish."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Continue"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Show variants"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Hide variants"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Choose a different event"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Choose a different date"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Back"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Next month"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Previous month"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Next week"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Previous week"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Open seat selection"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
@@ -1117,138 +1061,111 @@ msgid ""
|
||||
"again."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Load more"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Mo"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Tu"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "We"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Th"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Fr"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Sa"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Su"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Monday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Tuesday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Wednesday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Thursday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Friday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Saturday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Sunday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "January"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "February"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "March"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "April"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "May"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "June"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "July"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "August"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "September"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "October"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "November"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "December"
|
||||
msgstr ""
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-07-06 15:52+0000\n"
|
||||
"POT-Creation-Date: 2026-09-30 07:26+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -766,194 +766,163 @@ msgstr ""
|
||||
msgid "Google Pay"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Quantity"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Decrease quantity"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Increase quantity"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Filter events by"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Filter"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Price"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "Original price: %s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "New price: %s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Select"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "Select %s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "Select variant %s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Sold out"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Buy"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Register"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Reserved"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "FREE"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "from %(currency)s %(price)s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "Image of %s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "incl. %(rate)s% %(taxname)s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "plus %(rate)s% %(taxname)s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "incl. taxes"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "plus taxes"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "currently available: %s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Only available with a voucher"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Not yet available"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Not available anymore"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Currently not available"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "minimum amount to order: %s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Close ticket shop"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "The ticket shop could not be loaded."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
@@ -961,25 +930,21 @@ msgid ""
|
||||
"in a new tab to continue."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Open ticket shop"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Checkout"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "The cart could not be created. Please try again later"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
@@ -987,13 +952,11 @@ msgid ""
|
||||
"this ticket shop. Please click \"Continue\" to retry in a new tab."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Waiting list"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
@@ -1001,115 +964,96 @@ msgid ""
|
||||
"products, they will be added to your existing cart."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Resume checkout"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Redeem a voucher"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Redeem"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Voucher code"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Close checkout"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "You cannot cancel this operation. Please wait for loading to finish."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Continue"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Show variants"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Hide variants"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Choose a different event"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Choose a different date"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Back"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Next month"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Previous month"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Next week"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Previous week"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Open seat selection"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
@@ -1118,138 +1062,111 @@ msgid ""
|
||||
"again."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Load more"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Mo"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Tu"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "We"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Th"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Fr"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Sa"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Su"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Monday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Tuesday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Wednesday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Thursday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Friday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Saturday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Sunday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "January"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "February"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "March"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "April"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "May"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "June"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "July"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "August"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "September"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "October"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "November"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "December"
|
||||
msgstr ""
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-07-06 15:52+0000\n"
|
||||
"POT-Creation-Date: 2026-09-30 07:26+0000\n"
|
||||
"PO-Revision-Date: 2026-07-13 11:28+0000\n"
|
||||
"Last-Translator: ahmed badr <ahmedbadr@oma.com.eg>\n"
|
||||
"Language-Team: Arabic <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
@@ -648,7 +648,8 @@ msgstr "خطأ غير معروف."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js
|
||||
msgid "Your color has great contrast and will provide excellent accessibility."
|
||||
msgstr "يتميز لونك بتباين رائع ويسهل قراءته للغاية! وسيوفر إمكانية وصول ممتازة."
|
||||
msgstr ""
|
||||
"يتميز لونك بتباين رائع ويسهل قراءته للغاية! وسيوفر إمكانية وصول ممتازة."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js
|
||||
msgid ""
|
||||
@@ -663,8 +664,8 @@ msgid ""
|
||||
"Your color has insufficient contrast to white. Accessibility of your site "
|
||||
"will be impacted."
|
||||
msgstr ""
|
||||
"اللون الذي اخترته لا يتمتع بتباين كافٍ مع اللون الأبيض. وسيؤثر ذلك على إمكانية "
|
||||
"الوصول إلى موقعك."
|
||||
"اللون الذي اخترته لا يتمتع بتباين كافٍ مع اللون الأبيض. وسيؤثر ذلك على "
|
||||
"إمكانية الوصول إلى موقعك."
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js
|
||||
msgid "Search query"
|
||||
@@ -794,194 +795,163 @@ msgstr "التوقيت المحلي:"
|
||||
msgid "Google Pay"
|
||||
msgstr "Google Pay"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Quantity"
|
||||
msgstr "الكمية"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Decrease quantity"
|
||||
msgstr "تقليل الكمية"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Increase quantity"
|
||||
msgstr "زيادة الكمية"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Filter events by"
|
||||
msgstr "تصفية الأحداث حسب"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Filter"
|
||||
msgstr "فلتر"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Price"
|
||||
msgstr "السعر"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "Original price: %s"
|
||||
msgstr "السعر الأصلي: %s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "New price: %s"
|
||||
msgstr "السعر الجديد: %s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Select"
|
||||
msgstr "المختارة فقط"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "Select %s"
|
||||
msgstr "تم اختيار %s فقط"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "Select variant %s"
|
||||
msgstr "اختر النوع %s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Sold out"
|
||||
msgstr "تم البيع بالكامل"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Buy"
|
||||
msgstr "اشتري"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Register"
|
||||
msgstr "سجل"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Reserved"
|
||||
msgstr "محجوز"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "FREE"
|
||||
msgstr "مجاني"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "from %(currency)s %(price)s"
|
||||
msgstr "من %(currency) s %(price)s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "Image of %s"
|
||||
msgstr "صورة لـ %s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "incl. %(rate)s% %(taxname)s"
|
||||
msgstr "يشمل %(rate)s% %(taxname)s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "plus %(rate)s% %(taxname)s"
|
||||
msgstr "بالإضافة إلى %(rate)s% %(taxname)s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "incl. taxes"
|
||||
msgstr "شامل الضريبة"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "plus taxes"
|
||||
msgstr "بالإضافة إلى الضرائب"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "currently available: %s"
|
||||
msgstr "متوفر حاليا: %s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Only available with a voucher"
|
||||
msgstr "متوفرة مع القسيمة فقط"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Not yet available"
|
||||
msgstr "حاليًا غير متوفر: %s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Not available anymore"
|
||||
msgstr "لم يعد متوفراً"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Currently not available"
|
||||
msgstr "غير متوفر حاليًا: %s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "minimum amount to order: %s"
|
||||
msgstr "الحد الأدنى للطلب: %s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Close ticket shop"
|
||||
msgstr "إغلاق متجر التذاكر"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "The ticket shop could not be loaded."
|
||||
msgstr "لا يمكن تحميل متجر التذاكر."
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
@@ -991,25 +961,21 @@ msgstr ""
|
||||
"يوجد حالياً عدد كبير من المستخدمين في متجر التذاكر هذا. يرجى فتح المتجر في "
|
||||
"علامة تبويب جديدة للمتابعة."
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Open ticket shop"
|
||||
msgstr "إغلاق/فتح متجر التذاكر"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Checkout"
|
||||
msgstr "استئناف عملية الدفع"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "The cart could not be created. Please try again later"
|
||||
msgstr "لا يمكن إنشاء سلة التسوق. الرجاء المحاولة مرة أخرى في وقت لاحق"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
@@ -1017,16 +983,14 @@ msgid ""
|
||||
"this ticket shop. Please click \"Continue\" to retry in a new tab."
|
||||
msgstr ""
|
||||
"لم نتمكن من إنشاء سلة التسوق الخاصة بك، نظرًا لوجود عدد كبير جدًّا من "
|
||||
"المستخدمين حاليًّا في متجر التذاكر هذا. يرجى النقر على «متابعة» لإعادة المحاولة "
|
||||
"في علامة تبويب جديدة."
|
||||
"المستخدمين حاليًّا في متجر التذاكر هذا. يرجى النقر على «متابعة» لإعادة "
|
||||
"المحاولة في علامة تبويب جديدة."
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Waiting list"
|
||||
msgstr "قائمة الإنتظار"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
@@ -1036,115 +1000,96 @@ msgstr ""
|
||||
"لديك الآن سلة تسوق مفعلة لهذا الحدث. إذا قمت باختيار منتجات أخرى سيتم "
|
||||
"إضافتها إلى سلة التسوق الموجودة حالياً."
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Resume checkout"
|
||||
msgstr "استئناف الدفع"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Redeem a voucher"
|
||||
msgstr "استبدال قسيمة"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Redeem"
|
||||
msgstr "استبدال"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Voucher code"
|
||||
msgstr "رمز القسيمة"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Close"
|
||||
msgstr "إغلاق"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Close checkout"
|
||||
msgstr "إغلاق عملية الدفع"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "You cannot cancel this operation. Please wait for loading to finish."
|
||||
msgstr "لا يمكنك إلغاء هذه العملية. يرجى الانتظار حتى ينتهي التحميل."
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Continue"
|
||||
msgstr "استمرار"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Show variants"
|
||||
msgstr "انظر الاختلافات"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Hide variants"
|
||||
msgstr "إخفاء الاختلافات"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Choose a different event"
|
||||
msgstr "اختر فعالية أخرى"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Choose a different date"
|
||||
msgstr "اختر تاريخا آخر"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Back"
|
||||
msgstr "العودة"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Next month"
|
||||
msgstr "الشهر القادم"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Previous month"
|
||||
msgstr "الشهر السابق"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Next week"
|
||||
msgstr "الأسبوع القادم"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Previous week"
|
||||
msgstr "الأسبوع السابق"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Open seat selection"
|
||||
msgstr "خيارات المقاعد"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
@@ -1155,138 +1100,111 @@ msgstr ""
|
||||
"تم بيع جميع فئات التذاكر أو بعضها حاليًا. إذا أردت، يمكنك إضافة اسمك إلى "
|
||||
"قائمة الانتظار. وسنقوم بإخطارك إذا توفرت مقاعد مرة أخرى."
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Load more"
|
||||
msgstr "تحميل المزيد"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Mo"
|
||||
msgstr "الإثنين"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Tu"
|
||||
msgstr "الثلاثاء"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "We"
|
||||
msgstr "الأربعاء"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Th"
|
||||
msgstr "الخميس"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Fr"
|
||||
msgstr "الجمعة"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Sa"
|
||||
msgstr "السبت"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Su"
|
||||
msgstr "الأحد"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Monday"
|
||||
msgstr "الاثنين"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Tuesday"
|
||||
msgstr "الثلاثاء"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Wednesday"
|
||||
msgstr "الأربعاء"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Thursday"
|
||||
msgstr "الخميس"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Friday"
|
||||
msgstr "الجمعة"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Saturday"
|
||||
msgstr "السبت"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Sunday"
|
||||
msgstr "الأحد"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "January"
|
||||
msgstr "يناير"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "February"
|
||||
msgstr "فبراير"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "March"
|
||||
msgstr "مارس"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "April"
|
||||
msgstr "أبريل"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "May"
|
||||
msgstr "هناك"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "June"
|
||||
msgstr "يونيو"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "July"
|
||||
msgstr "يوليو"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "August"
|
||||
msgstr "أغسطس"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "September"
|
||||
msgstr "سبتمبر"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "October"
|
||||
msgstr "أكتوبر"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "November"
|
||||
msgstr "نوفمبر"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "December"
|
||||
msgstr "ديسمبر"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-07-06 15:52+0000\n"
|
||||
"POT-Creation-Date: 2026-09-30 07:26+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -766,194 +766,163 @@ msgstr ""
|
||||
msgid "Google Pay"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Quantity"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Decrease quantity"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Increase quantity"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Filter events by"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Filter"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Price"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "Original price: %s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "New price: %s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Select"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "Select %s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "Select variant %s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Sold out"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Buy"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Register"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Reserved"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "FREE"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "from %(currency)s %(price)s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "Image of %s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "incl. %(rate)s% %(taxname)s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "plus %(rate)s% %(taxname)s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "incl. taxes"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "plus taxes"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "currently available: %s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Only available with a voucher"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Not yet available"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Not available anymore"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Currently not available"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "minimum amount to order: %s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Close ticket shop"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "The ticket shop could not be loaded."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
@@ -961,25 +930,21 @@ msgid ""
|
||||
"in a new tab to continue."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Open ticket shop"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Checkout"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "The cart could not be created. Please try again later"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
@@ -987,13 +952,11 @@ msgid ""
|
||||
"this ticket shop. Please click \"Continue\" to retry in a new tab."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Waiting list"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
@@ -1001,115 +964,96 @@ msgid ""
|
||||
"products, they will be added to your existing cart."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Resume checkout"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Redeem a voucher"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Redeem"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Voucher code"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Close checkout"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "You cannot cancel this operation. Please wait for loading to finish."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Continue"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Show variants"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Hide variants"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Choose a different event"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Choose a different date"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Back"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Next month"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Previous month"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Next week"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Previous week"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Open seat selection"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
@@ -1118,138 +1062,111 @@ msgid ""
|
||||
"again."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Load more"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Mo"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Tu"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "We"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Th"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Fr"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Sa"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Su"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Monday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Tuesday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Wednesday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Thursday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Friday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Saturday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Sunday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "January"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "February"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "March"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "April"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "May"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "June"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "July"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "August"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "September"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "October"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "November"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "December"
|
||||
msgstr ""
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-07-06 15:52+0000\n"
|
||||
"POT-Creation-Date: 2026-09-30 07:26+0000\n"
|
||||
"PO-Revision-Date: 2025-10-31 17:00+0000\n"
|
||||
"Last-Translator: Núria Masclans <nuriamasclansserrat@gmail.com>\n"
|
||||
"Language-Team: Catalan <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
@@ -787,194 +787,163 @@ msgstr "La teva hora local:"
|
||||
msgid "Google Pay"
|
||||
msgstr "Google Pay"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Quantity"
|
||||
msgstr "Quantitat"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Decrease quantity"
|
||||
msgstr "Disminueix quantitat"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Increase quantity"
|
||||
msgstr "Augmenta quantitat"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Filter events by"
|
||||
msgstr "Filtra els esdeveniments per"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Filter"
|
||||
msgstr "Filtra"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Price"
|
||||
msgstr "Preu"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "Original price: %s"
|
||||
msgstr "Preu original: %s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "New price: %s"
|
||||
msgstr "Nou preu: %s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Select"
|
||||
msgstr "Selecciona"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "Select %s"
|
||||
msgstr "Selecciona %s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "Select variant %s"
|
||||
msgstr "Selecciona la variant %s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Sold out"
|
||||
msgstr "Esgotat"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Buy"
|
||||
msgstr "Compra"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Register"
|
||||
msgstr "Registrar-vos"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Reserved"
|
||||
msgstr "Reservat"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "FREE"
|
||||
msgstr "GRATUÏT"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "from %(currency)s %(price)s"
|
||||
msgstr "des de %(price)s %(currency)s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "Image of %s"
|
||||
msgstr "Imatge de %s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "incl. %(rate)s% %(taxname)s"
|
||||
msgstr "Inclòs %(rate)s% %(taxname)s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "plus %(rate)s% %(taxname)s"
|
||||
msgstr "més %(rate)s% %(taxname)s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "incl. taxes"
|
||||
msgstr "incl. impostos"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "plus taxes"
|
||||
msgstr "més impostos"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "currently available: %s"
|
||||
msgstr "disponibles: %s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Only available with a voucher"
|
||||
msgstr "Només disponible amb un cupó"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Not yet available"
|
||||
msgstr "Encara no disponible"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Not available anymore"
|
||||
msgstr "Ja no està disponible"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Currently not available"
|
||||
msgstr "No disponible"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "minimum amount to order: %s"
|
||||
msgstr "Comanda mínima: %s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Close ticket shop"
|
||||
msgstr "Tanca la botiga d'entrades"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "The ticket shop could not be loaded."
|
||||
msgstr "No s'ha pogut carregar la botiga d'entrades."
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
@@ -982,25 +951,21 @@ msgid ""
|
||||
"in a new tab to continue."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Open ticket shop"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Checkout"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "The cart could not be created. Please try again later"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
@@ -1008,13 +973,11 @@ msgid ""
|
||||
"this ticket shop. Please click \"Continue\" to retry in a new tab."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Waiting list"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
@@ -1022,115 +985,96 @@ msgid ""
|
||||
"products, they will be added to your existing cart."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Resume checkout"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Redeem a voucher"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Redeem"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Voucher code"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Close checkout"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "You cannot cancel this operation. Please wait for loading to finish."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Continue"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Show variants"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Hide variants"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Choose a different event"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Choose a different date"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Back"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Next month"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Previous month"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Next week"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Previous week"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Open seat selection"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
@@ -1139,138 +1083,111 @@ msgid ""
|
||||
"again."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Load more"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Mo"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Tu"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "We"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Th"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Fr"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Sa"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Su"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Monday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Tuesday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Wednesday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Thursday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Friday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Saturday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Sunday"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "January"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "February"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "March"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "April"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "May"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "June"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "July"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "August"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "September"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "October"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "November"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "December"
|
||||
msgstr ""
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-07-06 15:52+0000\n"
|
||||
"POT-Creation-Date: 2026-09-30 07:26+0000\n"
|
||||
"PO-Revision-Date: 2026-01-08 04:00+0000\n"
|
||||
"Last-Translator: Jiří Pastrňák <jiri@pastrnak.email>\n"
|
||||
"Language-Team: Czech <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
@@ -801,194 +801,163 @@ msgstr "Místní čas:"
|
||||
msgid "Google Pay"
|
||||
msgstr "Google Pay"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Quantity"
|
||||
msgstr "Počet"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Decrease quantity"
|
||||
msgstr "Snížit počet"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Increase quantity"
|
||||
msgstr "Zvýšit počet"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Filter events by"
|
||||
msgstr "Filtrovat události"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Filter"
|
||||
msgstr "Filtrovat"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Price"
|
||||
msgstr "Cena"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "Original price: %s"
|
||||
msgstr "Původní cena: %s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "New price: %s"
|
||||
msgstr "Nová cena: %s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Select"
|
||||
msgstr "Vybrat"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "Select %s"
|
||||
msgstr "Vybrat %s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "Select variant %s"
|
||||
msgstr "Vybrat variantu %s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Sold out"
|
||||
msgstr "Vyprodáno"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Buy"
|
||||
msgstr "Koupit"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Register"
|
||||
msgstr "Zaregistrovat"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Reserved"
|
||||
msgstr "Rezervováno"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "FREE"
|
||||
msgstr "ZDARMA"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "from %(currency)s %(price)s"
|
||||
msgstr "od %(currency)s %(price)s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "Image of %s"
|
||||
msgstr "Obrázek%s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "incl. %(rate)s% %(taxname)s"
|
||||
msgstr "včetně %(rate)s% %(taxname)s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "plus %(rate)s% %(taxname)s"
|
||||
msgstr "plus %(rate)s% %(taxname)s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "incl. taxes"
|
||||
msgstr "včetně daní"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "plus taxes"
|
||||
msgstr "plus daně"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "currently available: %s"
|
||||
msgstr "aktuálně k dispozici: %s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Only available with a voucher"
|
||||
msgstr "K dispozici pouze s poukazem"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Not yet available"
|
||||
msgstr "Zatím není k dispozici"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Not available anymore"
|
||||
msgstr "Již není k dispozici"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Currently not available"
|
||||
msgstr "Momentálně není k dispozici"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "minimum amount to order: %s"
|
||||
msgstr "minimální množství pro objednávku: %s"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Close ticket shop"
|
||||
msgstr "Zavřít obchod"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "The ticket shop could not be loaded."
|
||||
msgstr "Obchod vstupenek nemohl být načten."
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
@@ -998,25 +967,21 @@ msgstr ""
|
||||
"V současné době je v tomto obchodě s vstupenkami mnoho uživatelů. Otevřete "
|
||||
"prosím obchod v nové kartě."
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Open ticket shop"
|
||||
msgstr "Obchod vstupenek otevřit"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Checkout"
|
||||
msgstr "Přejít k platbě"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "The cart could not be created. Please try again later"
|
||||
msgstr "Nákupní košík se nepodařilo vytvořit. Zkuste to prosím znovu"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
@@ -1027,13 +992,11 @@ msgstr ""
|
||||
"mnoho uživatelů. Klikněte prosím na \"Pokračovat\" a zkuste to znovu na nové "
|
||||
"kartě."
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Waiting list"
|
||||
msgstr "Čekací listina"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
@@ -1043,37 +1006,31 @@ msgstr ""
|
||||
"V současné době máte aktivní košík pro tuto událost. Pokud vyberete další "
|
||||
"produkty, budou přidány do vašeho košíku."
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Resume checkout"
|
||||
msgstr "Obnovit checkout"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Redeem a voucher"
|
||||
msgstr "Uplatnit poukázku"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Redeem"
|
||||
msgstr "Uplatnit"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Voucher code"
|
||||
msgstr "Kód poukázky"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Close"
|
||||
msgstr "Zavřít"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
@@ -1082,79 +1039,66 @@ msgctxt "widget"
|
||||
msgid "Close checkout"
|
||||
msgstr "Obnovit checkout"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "You cannot cancel this operation. Please wait for loading to finish."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Continue"
|
||||
msgstr "Pokračovat"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Show variants"
|
||||
msgstr "Zobrazit možnosti"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Hide variants"
|
||||
msgstr "Skrýt možnosti"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Choose a different event"
|
||||
msgstr "Vybrat jinou událost"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Choose a different date"
|
||||
msgstr "Vybrat jiný datum"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Back"
|
||||
msgstr "Zpět"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Next month"
|
||||
msgstr "Následující měsíc"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Previous month"
|
||||
msgstr "Předchozí měsíc"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Next week"
|
||||
msgstr "Příští týden"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Previous week"
|
||||
msgstr "Předchozí týden"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Open seat selection"
|
||||
msgstr "Otevřete výběr míst"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
@@ -1166,138 +1110,111 @@ msgstr ""
|
||||
"Pokud chcete, můžete se přidat na čekací listinu. Poté vás budeme "
|
||||
"informovat, pokud budou místa opět volná."
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgctxt "widget"
|
||||
msgid "Load more"
|
||||
msgstr "Načíst více"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Mo"
|
||||
msgstr "Po"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Tu"
|
||||
msgstr "Út"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "We"
|
||||
msgstr "St"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Th"
|
||||
msgstr "Čt"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Fr"
|
||||
msgstr "Pá"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Sa"
|
||||
msgstr "So"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Su"
|
||||
msgstr "Ne"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Monday"
|
||||
msgstr "Pondělí"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Tuesday"
|
||||
msgstr "Úterý"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Wednesday"
|
||||
msgstr "Středa"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Thursday"
|
||||
msgstr "Čtvrtek"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Friday"
|
||||
msgstr "Pátek"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Saturday"
|
||||
msgstr "Sobota"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "Sunday"
|
||||
msgstr "Neděle"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "January"
|
||||
msgstr "Leden"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "February"
|
||||
msgstr "Únor"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "March"
|
||||
msgstr "Březen"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "April"
|
||||
msgstr "Duben"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "May"
|
||||
msgstr "Květen"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "June"
|
||||
msgstr "červen"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "July"
|
||||
msgstr "Červenec"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "August"
|
||||
msgstr "Srpen"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "September"
|
||||
msgstr "Září"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "October"
|
||||
msgstr "Říjen"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "November"
|
||||
msgstr "Listopad"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js
|
||||
#: pretix/static/pretixpresale/widget/src/i18n.ts
|
||||
msgid "December"
|
||||
msgstr "Prosinec"
|
||||
|
||||
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