Compare commits

..
Author SHA1 Message Date
Mira Weller 43b9049a12 Fix test cases for checkinlist export 2024-07-12 10:53:37 +02:00
249 changed files with 98726 additions and 151595 deletions
+1
View File
@@ -5,6 +5,7 @@ on:
branches: [ master ] branches: [ master ]
paths-ignore: paths-ignore:
- 'doc/**' - 'doc/**'
- 'src/pretix/locale/**'
pull_request: pull_request:
branches: [ master ] branches: [ master ]
paths-ignore: paths-ignore:
-1
View File
@@ -1 +0,0 @@
17
-2
View File
@@ -10,8 +10,6 @@ recursive-include src/pretix/helpers/locale *
recursive-include src/pretix/base/templates * recursive-include src/pretix/base/templates *
recursive-include src/pretix/control/templates * recursive-include src/pretix/control/templates *
recursive-include src/pretix/presale/templates * recursive-include src/pretix/presale/templates *
recursive-include src/pretix/plugins/autocheckin/templates *
recursive-include src/pretix/plugins/autocheckin/static *
recursive-include src/pretix/plugins/banktransfer/templates * recursive-include src/pretix/plugins/banktransfer/templates *
recursive-include src/pretix/plugins/banktransfer/static * recursive-include src/pretix/plugins/banktransfer/static *
recursive-include src/pretix/plugins/manualpayment/templates * recursive-include src/pretix/plugins/manualpayment/templates *
+1 -1
View File
@@ -65,7 +65,7 @@ Package dependencies
To build and run pretix, you will need the following debian packages:: To build and run pretix, you will need the following debian packages::
# apt-get install git build-essential python3-dev python3-venv python3 python3-pip \ # apt-get install git build-essential python3-dev python3-venv python3 python3-pip \
libxml2-dev libxslt1-dev libffi-dev zlib1g-dev libssl-dev \ python3-dev libxml2-dev libxslt1-dev libffi-dev zlib1g-dev libssl-dev \
gettext libpq-dev libjpeg-dev libopenjp2-7-dev gettext libpq-dev libjpeg-dev libopenjp2-7-dev
Config file Config file
-7
View File
@@ -73,11 +73,4 @@ This release includes a migration that changes retroactively fills an `organizer
`pretixbase_logentry`. If you have a large database, the migration step of the upgrade might take significantly `pretixbase_logentry`. If you have a large database, the migration step of the upgrade might take significantly
longer than usual, so plan the update accordingly. longer than usual, so plan the update accordingly.
Upgrade to 2024.7.0 or newer
"""""""""""""""""""""""""""""
This release includes a migration that changes how sales channels are referred on orders.
If you have a large database, the migration step of the upgrade might take significantly longer than usual, so plan
the update accordingly.
.. _blog: https://pretix.eu/about/en/blog/ .. _blog: https://pretix.eu/about/en/blog/
-259
View File
@@ -1,259 +0,0 @@
.. _rest-autocheckinrules:
Auto check-in rules
===================
This feature requires the bundled ``pretix.plugins.autocheckin`` plugin to be active for the event in order to work properly.
Resource description
--------------------
Auto check-in rules specify that tickets should under specific conditions automatically be considered checked in after
they have been purchased.
.. rst-class:: rest-resource-table
===================================== ========================== =======================================================
Field Type Description
===================================== ========================== =======================================================
id integer Internal ID of the rule
list integer ID of the check-in list to check the ticket in on. If
``None``, the system will select all matching check-in lists.
mode string ``"placed"`` if the rule should be evaluated right after
an order has been created, ``"paid"`` if the rule should
be evaluated after the order has been fully paid.
all_sales_channels boolean If ``true`` (default), the rule applies to tickets sold on all sales channels.
limit_sales_channels list of strings List of sales channel identifiers the rule should apply to
if ``all_sales_channels`` is ``false``.
all_products boolean If ``true`` (default), the rule affects all products and variations.
limit_products list of integers List of item IDs, if ``all_products`` is not set. If the
product listed here has variations, all variations will be matched.
limit_variations list of integers List of product variation IDs, if ``all_products`` is not set.
The parent product does not need to be part of ``limit_products``.
all_payment_methods boolean If ``true`` (default), the rule applies to tickets paid with all payment methods.
limit_payment_methods list of strings List of payment method identifiers the rule should apply to
if ``all_payment_methods`` is ``false``.
===================================== ========================== =======================================================
.. versionadded:: 2024.7
Endpoints
---------
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/auto_checkin_rules/
Returns a list of all rules configured for an event.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/auto_checkin_rules/ 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,
"list": 12345,
"mode": "placed",
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"all_products": False,
"limit_products": [2, 3],
"limit_variations": [456],
"all_payment_methods": true,
"limit_payment_methods": []
}
]
}
:query page: The page number in case of a multi-page result set, default is 1
:param organizer: The ``slug`` field of a valid organizer
:param event: The ``slug`` field of the event to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer does not exist **or** you have no permission to view it.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/auto_checkin_rules/(id)/
Returns information on one rule, identified by its ID.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/auto_checkin_rules/1/ 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
{
"id": 1,
"list": 12345,
"mode": "placed",
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"all_products": False,
"limit_products": [2, 3],
"limit_variations": [456],
"all_payment_methods": true,
"limit_payment_methods": []
}
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param id: The ``id`` field of the rule to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event/rule does not exist **or** you have no permission to view it.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/auto_checkin_rules/
Create a new rule.
**Example request**:
.. sourcecode:: http
POST /api/v1/organizers/bigevents/events/sampleconf/auto_checkin_rules/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 166
{
"list": 12345,
"mode": "placed",
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"all_products": False,
"limit_products": [2, 3],
"limit_variations": [456],
"all_payment_methods": true,
"limit_payment_methods": []
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 201 Created
Vary: Accept
Content-Type: application/json
{
"id": 1,
"list": 12345,
"mode": "placed",
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"all_products": False,
"limit_products": [2, 3],
"limit_variations": [456],
"all_payment_methods": true,
"limit_payment_methods": []
}
:param organizer: The ``slug`` field of the organizer to create a rule for
:param event: The ``slug`` field of the event to create a rule for
:statuscode 201: no error
:statuscode 400: The rule could not be created due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create rules.
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/auto_checkin_rules/(id)/
Update a rule. 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.
**Example request**:
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/events/sampleconf/auto_checkin_rules/1/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
Content-Length: 34
{
"mode": "paid",
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: text/javascript
{
"id": 1,
"list": 12345,
"mode": "placed",
"all_sales_channels": false,
"limit_sales_channels": ["web"],
"all_products": False,
"limit_products": [2, 3],
"limit_variations": [456],
"all_payment_methods": true,
"limit_payment_methods": []
}
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param id: The ``id`` field of the rule to modify
:statuscode 200: no error
:statuscode 400: The rule could not be modified due to invalid submitted data.
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event/rule does not exist **or** you have no permission to change it.
.. http:delete:: /api/v1/organizers/(organizer)/events/(event)/auto_checkin_rules/(id)/
Delete a rule.
**Example request**:
.. sourcecode:: http
DELETE /api/v1/organizers/bigevents/events/sampleconf/auto_checkin_rules/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 to modify
:param event: The ``slug`` field of the event to modify
:param id: The ``id`` field of the rule to delete
:statuscode 204: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event/rule does not exist **or** you have no permission to change it **or** this rule cannot be deleted since it is currently in use.
-1
View File
@@ -32,7 +32,6 @@ position_count integer Number of ticke
checkin_count integer Number of check-ins performed on this list (read-only). checkin_count integer Number of check-ins performed on this list (read-only).
include_pending boolean If ``true``, the check-in list also contains tickets from orders in pending state. include_pending boolean If ``true``, the check-in list also contains tickets from orders in pending state.
auto_checkin_sales_channels list of strings All items on the check-in list will be automatically marked as checked-in when purchased through any of the listed sales channels. auto_checkin_sales_channels list of strings All items on the check-in list will be automatically marked as checked-in when purchased through any of the listed sales channels.
**Deprecated, will be removed in pretix 2024.10.** Use :ref:`rest-autocheckinrules`: instead.
allow_multiple_entries boolean If ``true``, subsequent scans of a ticket on this list should not show a warning but instead be stored as an additional check-in. allow_multiple_entries boolean If ``true``, subsequent scans of a ticket on this list should not show a warning but instead be stored as an additional check-in.
allow_entry_after_exit boolean If ``true``, subsequent scans of a ticket on this list are valid if the last scan of the ticket was an exit scan. allow_entry_after_exit boolean If ``true``, subsequent scans of a ticket on this list are valid if the last scan of the ticket was an exit scan.
rules object Custom check-in logic. The contents of this field are currently not considered a stable API and modifications through the API are highly discouraged. rules object Custom check-in logic. The contents of this field are currently not considered a stable API and modifications through the API are highly discouraged.
+1 -3
View File
@@ -44,7 +44,5 @@ at :ref:`plugin-docs`.
scheduled_exports scheduled_exports
shredders shredders
sendmail_rules sendmail_rules
auto_checkin_rules
billing_invoices billing_invoices
billing_var billing_var
seats
-3
View File
@@ -217,9 +217,6 @@ List of all invoices
:query boolean is_cancellation: If set to ``true`` or ``false``, only invoices with this value for the field :query boolean is_cancellation: If set to ``true`` or ``false``, only invoices with this value for the field
``is_cancellation`` will be returned. ``is_cancellation`` will be returned.
:query string order: If set, only invoices belonging to the order with the given order code will be returned. :query string order: If set, only invoices belonging to the order with the given order code will be returned.
This parameter may be given multiple times. In this case, all invoices matching one of the inputs will be returned.
:query string number: If set, only invoices with the given invoice number will be returned.
This parameter may be given multiple times. In this case, all invoices matching one of the inputs will be returned.
:query string refers: If set, only invoices referring to the given invoice will be returned. :query string refers: If set, only invoices referring to the given invoice will be returned.
:query string locale: If set, only invoices with the given locale will be returned. :query string locale: If set, only invoices with the given locale will be returned.
:query string ordering: Manually set the ordering of results. Valid fields to be used are ``date`` and :query string ordering: Manually set the ordering of results. Valid fields to be used are ``date`` and
+1 -7
View File
@@ -42,8 +42,6 @@ payment_date date **DEPRECATED AN
payment_provider string **DEPRECATED AND INACCURATE** Payment provider used for this order payment_provider string **DEPRECATED AND INACCURATE** Payment provider used for this order
total money (string) Total value of this order total money (string) Total value of this order
comment string Internal comment on this order comment string Internal comment on this order
api_meta object Meta data for that order. Only available through API, no guarantees
on the content structure. You can use this to save references to your system.
custom_followup_at date Internal date for a custom follow-up action custom_followup_at date Internal date for a custom follow-up action
checkin_attention boolean If ``true``, the check-in app should show a warning checkin_attention boolean If ``true``, the check-in app should show a warning
that this ticket requires special attention if a ticket that this ticket requires special attention if a ticket
@@ -203,8 +201,7 @@ checkins list of objects List of **succe
├ datetime datetime Time of check-in ├ datetime datetime Time of check-in
├ type string Type of scan (defaults to ``entry``) ├ type string Type of scan (defaults to ``entry``)
├ gate integer Internal ID of the gate. Can be ``null``. ├ gate integer Internal ID of the gate. Can be ``null``.
├ device integer Internal ID of the device. Can be ``null``. **Deprecated**, since this ID is not otherwise used in the API and is therefore not very useful. ├ device integer Internal ID of the device. Can be ``null``.
├ device_id integer Attribute ``device_id`` of the device. Can be ``null``.
└ auto_checked_in boolean Indicates if this check-in been performed automatically by the system └ auto_checked_in boolean Indicates if this check-in been performed automatically by the system
downloads list of objects List of ticket download options downloads list of objects List of ticket download options
├ output string Ticket output provider (e.g. ``pdf``, ``passbook``) ├ output string Ticket output provider (e.g. ``pdf``, ``passbook``)
@@ -565,7 +562,6 @@ Fetching individual orders
"fees": [], "fees": [],
"total": "23.00", "total": "23.00",
"comment": "", "comment": "",
"api_meta": {},
"custom_followup_at": null, "custom_followup_at": null,
"checkin_attention": false, "checkin_attention": false,
"checkin_text": null, "checkin_text": null,
@@ -746,8 +742,6 @@ Updating order fields
* ``comment`` * ``comment``
* ``api_meta``
* ``custom_followup_at`` * ``custom_followup_at``
* ``invoice_address`` (you always need to supply the full object, or ``null`` to delete the current address) * ``invoice_address`` (you always need to supply the full object, or ``null`` to delete the current address)
-262
View File
@@ -1,262 +0,0 @@
.. _`rest-reusablemedia`:
Seats
=====
The seat resource represents the seats in a seating plan in a specific event or subevent.
Resource description
--------------------
The seat resource contains the following public fields:
.. rst-class:: rest-resource-table
===================================== ========================== =======================================================
Field Type Description
===================================== ========================== =======================================================
id integer Internal ID of this seat
subevent integer Internal ID of the subevent this seat belongs to
zone_name string Name of the zone the seat is in
row_name string Name/number of the row the seat is in
row_label string Additional label of the row (or ``null``)
seat_number string Number of the seat within the row
seat_label string Additional label of the seat (or ``null``)
seat_guid string Identifier of the seat within the seating plan
product integer Internal ID of the product that is mapped to this seat
blocked boolean Whether this seat is blocked manually.
orderposition integer / object Internal ID of an order position reserving this seat.
cartposition integer / object Internal ID of a cart position reserving this seat.
voucher integer / object Internal ID of a voucher reserving this seat.
===================================== ========================== =======================================================
Endpoints
---------
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/seats/
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/subevents/(subevent_id)/seats/
Returns a list of all seats in the specified event or subevent. Depending on whether the event has subevents, the
according endpoint has to be used.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/seats/ HTTP/1.1
Host: pretix.eu
Accept: application/json
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"count": 500,
"next": "https://pretix.eu/api/v1/organizers/bigevents/events/sampleconf/seats/?page=2",
"previous": null,
"results": [
{
"id": 1633,
"subevent": null,
"zone_name": "Ground floor",
"row_name": "1",
"row_label": null,
"seat_number": "1",
"seat_label": null,
"seat_guid": "b9746230-6f31-4f41-bbc9-d6b60bdb3342",
"product": 104,
"blocked": false,
"orderposition": null,
"cartposition": null,
"voucher": 51
},
{
"id": 1634,
"subevent": null,
"zone_name": "Ground floor",
"row_name": "1",
"row_label": null,
"seat_number": "2",
"seat_label": null,
"seat_guid": "1d29fe20-8e1e-4984-b0ee-2773b0d07e07",
"product": 104,
"blocked": true,
"orderposition": 4321,
"cartposition": null,
"voucher": null
},
// ...
]
}
:query integer page: The page number in case of a multi-page result set, default is 1.
:query string zone_name: Only show seats with the given zone_name.
:query string row_name: Only show seats with the given row_name.
:query string row_label: Only show seats with the given row_label.
:query string seat_number: Only show seats with the given seat_number.
:query string seat_label: Only show seats with the given seat_label.
:query string seat_guid: Only show seats with the given seat_guid.
:query boolean blocked: Only show seats with the given blocked status.
:query boolean is_available: Only show seats that are (not) currently available.
:query string expand: If you pass ``"orderposition"``, ``"cartposition"``, or ``"voucher"``, the respective field will be
shown as a nested value instead of just an ID. This requires permission to access that object.
The nested objects are identical to the respective resources, except that order positions
will have an attribute of the format ``"order": {"code": "ABCDE", "event": "eventslug"}`` to make
matching easier, and won't include the `seat` attribute, as that would be redundant.
The parameter can be given multiple times.
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param subevent_id: The ``id`` field of the subevent to fetch
: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.
:statuscode 404: Endpoint without subevent id was used for event with subevents, or vice versa.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/seats/(id)/
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/subevents/(subevent_id)/seats/(id)/
Returns information on one seat, identified by its ID.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/seats/1634/?expand=orderposition HTTP/1.1
Host: pretix.eu
Accept: application/json
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 1634,
"subevent": null,
"zone_name": "Ground floor",
"row_name": "1",
"row_label": null,
"seat_number": "2",
"seat_label": null,
"seat_guid": "1d29fe20-8e1e-4984-b0ee-2773b0d07e07",
"product": 104,
"blocked": true,
"orderposition": {
"id": 134,
"order": {
"code": "U0HW7",
"event": "sampleconf"
},
"positionid": 1,
"item": 104,
"variation": 59,
"price": "60.00",
"attendee_name": "",
"attendee_name_parts": {
"_scheme": "given_family"
},
"company": null,
"street": null,
"zipcode": null,
"city": null,
"country": null,
"state": null,
"discount": null,
"attendee_email": null,
"voucher": null,
"tax_rate": "0.00",
"tax_value": "0.00",
"secret": "4rfgp263jduratnsvwvy6cc6r6wnptbj",
"addon_to": null,
"subevent": null,
"checkins": [],
"downloads": [],
"answers": [],
"tax_rule": null,
"pseudonymization_id": "ZSNYSG3URZ",
"canceled": false,
"valid_from": null,
"valid_until": null,
"blocked": null,
"voucher_budget_use": null
},
"cartposition": null,
"voucher": null
}
:param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch
:param subevent_id: The ``id`` field of the subevent to fetch
:param id: The ``id`` field of the seat to fetch
:query string expand: If you pass ``"orderposition"``, ``"cartposition"``, or ``"voucher"``, the respective field will be
shown as a nested value instead of just an ID. This requires permission to access that object.
The nested objects are identical to the respective resources, except that order positions
will have an attribute of the format ``"order": {"code": "ABCDE", "event": "eventslug"}`` to make
matching easier, and won't include the `seat` attribute, as that would be redundant.
The parameter can be given multiple times.
: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.
:statuscode 404: Seat does not exist; or the endpoint without subevent id was used for event with subevents, or vice versa.
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/seats/(id)/
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/subevents/(id)/seats/(id)/
Update a seat.
You can only change the ``blocked`` field.
**Example request**:
.. sourcecode:: http
PATCH /api/v1/organizers/bigevents/events/sampleconf/seats/1636/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
Content-Type: application/json
{
"blocked": true
}
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"id": 1636,
"subevent": null,
"zone_name": "Ground floor",
"row_name": "1",
"row_label": null,
"seat_number": "4",
"seat_label": null,
"seat_guid": "6c0e29e5-05d6-421f-99f3-afd01478ecad",
"product": 104,
"blocked": true,
"orderposition": null,
"cartposition": null,
"voucher": null
},
:param organizer: The ``slug`` field of the organizer to modify
:param event: The ``slug`` field of the event to modify
:param subevent_id: The ``id`` field of the subevent to modify
:param id: The ``id`` field of the seat to modify
:statuscode 200: no error
:statuscode 400: The seat could not be modified due to invalid submitted data
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer or event does not exist **or** you have no permission to change this resource.
:statuscode 404: Seat does not exist; or the endpoint without subevent id was used for event with subevents, or vice versa.
-3
View File
@@ -1,8 +1,6 @@
Scheduled email rules Scheduled email rules
===================== =====================
This feature requires the bundled ``pretix.plugins.sendmail`` plugin to be active for the event in order to work properly.
Resource description Resource description
-------------------- --------------------
@@ -50,7 +48,6 @@ send_to string Can be ``"order
or ``"both"``. or ``"both"``.
date. Otherwise it is relative to the event start date. date. Otherwise it is relative to the event start date.
===================================== ========================== ======================================================= ===================================== ========================== =======================================================
.. versionchanged:: 2023.7 .. versionchanged:: 2023.7
The ``include_pending`` field has been deprecated. The ``include_pending`` field has been deprecated.
-2
View File
@@ -41,7 +41,6 @@ The following values for ``action_types`` are valid with pretix core:
* ``pretix.event.order.modified`` * ``pretix.event.order.modified``
* ``pretix.event.order.contact.changed`` * ``pretix.event.order.contact.changed``
* ``pretix.event.order.changed.*`` * ``pretix.event.order.changed.*``
* ``pretix.event.order.deleted`` (can only occur for test mode orders)
* ``pretix.event.order.refund.created`` * ``pretix.event.order.refund.created``
* ``pretix.event.order.refund.created.externally`` * ``pretix.event.order.refund.created.externally``
* ``pretix.event.order.refund.requested`` * ``pretix.event.order.refund.requested``
@@ -116,7 +115,6 @@ Endpoints
} }
:query integer page: The page number in case of a multi-page result set, default is 1 :query integer page: The page number in case of a multi-page result set, default is 1
:query boolean enabled: Only show webhooks that are or are not enabled
:param organizer: The ``slug`` field of the organizer to fetch :param organizer: The ``slug`` field of the organizer to fetch
:statuscode 200: no error :statuscode 200: no error
:statuscode 401: Authentication failure :statuscode 401: Authentication failure
+1 -1
View File
@@ -39,7 +39,7 @@ Frontend
.. automodule:: pretix.presale.signals .. automodule:: pretix.presale.signals
:members: order_info, order_info_top, order_meta_from_request, order_api_meta_from_request :members: order_info, order_info_top, order_meta_from_request
Request flow Request flow
"""""""""""" """"""""""""
+3 -1
View File
@@ -136,7 +136,9 @@ It is a good idea to put this command into your git hook ``.git/hooks/pre-commit
for example, to check for any errors in any staged files when committing:: for example, to check for any errors in any staged files when committing::
#!/bin/bash #!/bin/bash
cd $GIT_DIR/../src
export GIT_WORK_TREE=../
export GIT_DIR=../.git
source ../env/bin/activate # Adjust to however you activate your virtual environment source ../env/bin/activate # Adjust to however you activate your virtual environment
for file in $(git diff --cached --name-only | grep -E '\.py$' | grep -Ev "migrations|mt940\.py|pretix/settings\.py|make_testdata\.py|testutils/settings\.py|tests/settings\.py|pretix/base/models/__init__\.py|.*_pb2\.py") for file in $(git diff --cached --name-only | grep -E '\.py$' | grep -Ev "migrations|mt940\.py|pretix/settings\.py|make_testdata\.py|testutils/settings\.py|tests/settings\.py|pretix/base/models/__init__\.py|.*_pb2\.py")
do do
-105
View File
@@ -1,105 +0,0 @@
GetYourGuide
============
.. note::
The GetYourGuide integration is currently in Beta. Please contact support@pretix.eu to enable the integration
for your pretix.eu organizer account.
Introduction
------------
Using third party aggregators, such als GetYourGuide, event organizers can sell tickets to their events not only on
their own ticket-shop but also on the aggregator's portal. While this service is not for free, it allows event
organizers to reacher a larger audience that would otherwise not have found their way into the organizers webshop.
Using pretix' integration with GetYourGuide, event organizers can profit from an additional sales and revenue channel,
while keeping the effort for setting up and maintaining multiple ticket shops to a minimum.
Preparing your organizer account
--------------------------------
The first step in enabling the GetYourGuide integration, is to setup a corresponding Sales Channel, which will be used
to properly attribute the sales generated. This needs to be done only once per organizer account.
To do so, log into the pretix backend, select ``Organizers`` from the navigation and then the organizer in question.
Extending the ``Settings``-menu, find the ``Sales channels`` configuration and click the ``Add a new channel`` button.
On the following page, you will be able to select ``GetYourGuide`` as the sales channel type and give it a custom name.
Preparing your event
--------------------
In order to now sell your events on GetYourGuide, you will need to configure each event in question.
1. Enabling the plugin
Within your event, extend the ``Settings`` menu and navigate to ``Plugins``. Activate the plugin in the
``Integrations`` tab.
2. Sell the event on the sales channel
Pick the sales channel or channels, on which you would like to sell your event by navigating to the event's general
settings page using the ``Sell on all sales channels`` or ``Restrict to specific sales channels`` checkboxes.
3. Configure one or more products to be sold on GetYourGuide
Either create a new or edit an existing product, that you would like to sell on GetYourGuide. To do so, you will
need to have checked the ``Sell on all sales channels`` or appropriate ``Restrict to specific sales channels``
checkbox of the product within it's ``Availability`` tab.
In addition, you will also need to set the GetYourGuide equivalent ticket category in the product's accordingly
named settings tab. Within your event, there can be only one product per ticket category. Depending on your further
configuration, you must at least select one product to be in the ``Adult`` or ``Group`` category.
4. Configuring the GetYourGuide-plugin
Once you have configured one or more products to be eligible to be sold on GetYourGuide, you'll need to configure a
few basic settings within the event (``Settings`` --> ``GetYourGuide``). The most important settings can be found
the in the ``Configuration`` tab, such as the location of the event on sale.
Ticket Categories
-----------------
While pretix only uses the ticket category term loosely to group together multiple products for nicer display,
GetYourGuide is relying on the ticket categories to price the tickets.
First of all, you need to make the decision on how you are planning on selling your tickets on GetYourGuide - in most
cases, this will reflect your current sales strategy within your pretix shop.
- Individual tickets
Every single person attending will need to purchase their own ticket. A family of two adults and two
children will have to purchase and pay for a total of 4 tickets.
In this case, you will need to offer *at least* a ticket of the ``Adult`` type, but may offer any other ticket
category type (Child, Youth, Senior, ...) in addition. But you cannot offer a ``Group`` ticket.
- Group tickets
Two groups, consisting of 10 and 20 participants respectively, won't need to purchase a total of 30 tickets, but
rather two group tickets. It is up to you to configure the group size limits within the GetYourGuide-settings of your
product.
Choosing this option, you cannot offer any other ticket categories besides ``Group``.
Setting up event dates and quotas
---------------------------------
Of course, in addition to creating products, you will also need to add them to a quota for them to be available for
sale. The process for doing this is the very same as for any regular event or event series.
.. note::
When selling individual tickets through GetYourGuide, you will not be able to offer differing quantities for
individual ticket categories.
For this reason, we recommend to place all GetYourGuide-eligible products into the same quota. Should you however opt
to create multiple quotas which create an imbalance, pretix will report only the available number of tickets for the
lowest relevant quota.
Connecting your event to GetYourGuide
-------------------------------------
Once you have set up your event and products and performed all necessary configuration, you may want to use the
Analyzer-feature of our GetYourGuide-plugin (``Settings`` -> ``GetYourGuide`` -> tab ``Analyzer``).
The Analyzer should not display any blocking error messages and at least one event date that is ready for publishing on
the GetYourGuide platform.
At this point, you will need to setup your event (called ``product`` in the GetYourGuide universe) on their
`Supplier Portal`_ and connect it with your pretix shop. To do so, please follow the
`Connecting a new product to your Reservation System`_ on the GetYourGuide Supply Partner Help Center.
Select ``pretix.eu`` as your reservation system; the required ``product ID`` can be found in the ``Configuration`` tab
of the GetYourGuide plugin settings page.
From this point on, GetYourGuide will automatically import the availabilities and products and offer them for sale.
.. _Supplier Portal: https://suppliers.getyourguide.com/
.. _Connecting a new product to your Reservation System: https://supply.getyourguide.support/hc/en-us/articles/18008029689373-Connecting-a-new-product-to-your-Reservation-system
-1
View File
@@ -25,4 +25,3 @@ If you want to **create** a plugin, please go to the
webinar webinar
presale-saml presale-saml
kulturpass kulturpass
getyourguide
+2 -1
View File
@@ -1,4 +1,4 @@
sphinx==7.4.* sphinx==7.3.*
jinja2==3.1.* jinja2==3.1.*
sphinx-rtd-theme sphinx-rtd-theme
sphinxcontrib-httpdomain sphinxcontrib-httpdomain
@@ -6,4 +6,5 @@ sphinxcontrib-images
sphinxcontrib-jquery sphinxcontrib-jquery
sphinxcontrib-spelling==8.* sphinxcontrib-spelling==8.*
sphinxemoji sphinxemoji
pygments-markdown-lexer
pyenchant==3.2.* pyenchant==3.2.*
+2 -1
View File
@@ -1,5 +1,5 @@
-e ../ -e ../
sphinx==7.4.* sphinx==7.3.*
jinja2==3.1.* jinja2==3.1.*
sphinx-rtd-theme sphinx-rtd-theme
sphinxcontrib-httpdomain sphinxcontrib-httpdomain
@@ -7,4 +7,5 @@ sphinxcontrib-images
sphinxcontrib-jquery sphinxcontrib-jquery
sphinxcontrib-spelling==8.* sphinxcontrib-spelling==8.*
sphinxemoji sphinxemoji
pygments-markdown-lexer
pyenchant==3.2.* pyenchant==3.2.*
+6 -3
View File
@@ -31,7 +31,8 @@ Android 9 Support planned until at least 12/2025.
Android 8 Support planned until at least 12/2025. Android 8 Support planned until at least 12/2025.
Android 7 Support planned until at least 06/2025. Android 7 Support planned until at least 06/2025.
Android 6 Support planned until at least 06/2025. Android 6 Support planned until at least 06/2025.
Android 5 Support planned until at least 06/2025. Android 5 | Support planned until at least 06/2025.
| No support for COVID certificate verification.
Android 4 Support dropped. Android 4 Support dropped.
=========================== ========================================================== =========================== ==========================================================
@@ -56,17 +57,16 @@ Android 8 | Support planned until at least 12/2025.
Android 7 | Support planned until at least 12/2024. Android 7 | Support planned until at least 12/2024.
| Support for Stripe Terminal to be dropped 05/2024. | Support for Stripe Terminal to be dropped 05/2024.
| No support for Cryptovision TSE. | No support for Cryptovision TSE.
| No support for SumUp.
Android 6 | Support planned until at least 12/2024. Android 6 | Support planned until at least 12/2024.
| No support for Cryptovision TSE. | No support for Cryptovision TSE.
| No support for Fiskal Cloud. | No support for Fiskal Cloud.
| No support for Stripe Terminal. | No support for Stripe Terminal.
| No support for SumUp.
Android 5 | Support planned until at least 12/2024. Android 5 | Support planned until at least 12/2024.
| No support for Cryptovision TSE. | No support for Cryptovision TSE.
| No support for Fiskal Cloud. | No support for Fiskal Cloud.
| No support for Stripe Terminal. | No support for Stripe Terminal.
| No support for SumUp. | No support for SumUp.
| No support for COVID certificate verification.
Android 4 Support dropped. Android 4 Support dropped.
=========================== ========================================================== =========================== ==========================================================
@@ -87,6 +87,9 @@ Android 7 Support planned until at least 06/2025.
Android 6 Support planned until at least 06/2025. Android 6 Support planned until at least 06/2025.
Android 5 | Support planned until at least 06/2025. Android 5 | Support planned until at least 06/2025.
| No support for Evolis printers on some devices. | No support for Evolis printers on some devices.
Android 4.4 | Support planned until at least 06/2024.
| No support for USB printers.
| No support for Evolis printers.
Android 4 Support dropped. Android 4 Support dropped.
=========================== ========================================================== =========================== ==========================================================
-23
View File
@@ -449,29 +449,6 @@ Further reading:
* `Stripe Payment Method Domain registration`_ * `Stripe Payment Method Domain registration`_
Content Security Policy
-----------------------
When using a Content Security Policy (CSP) on your website, you may need to make some adjustments. If your pretix
shop is running under a custom domain, you need to add the following rules:
* ``script-src``: ``'unsafe-eval' https://pretix.eu`` (adjust to your domain for self-hosted pretix)
* ``style-src``: ``https://pretix.eu`` (adjust to your domain for self-hosted pretix **and** for custom domain on pretix Hosted)
* ``connect-src``: ``https://pretix.eu`` (adjust to your domain for self-hosted pretix **and** for custom domain on pretix Hosted)
* ``frame-src``: ``https://pretix.eu`` (adjust to your domain for self-hosted pretix **and** for custom domain on pretix Hosted)
* ``img-src``: ``https://pretix.eu`` (adjust to your domain for self-hosted pretix **and** for custom domain on pretix Hosted) and for pretix Hosted additionally add ``https://cdn.pretix.space``
External payment providers and Cross-Origin-Opener-Policy
---------------------------------------------------------
If you use a payment provider that opens a new window during checkout (such as PayPal), be aware that setting
``Cross-Origin-Opener-Policy: same-origin`` results in an empty popup-window being opened in the foreground. This is
due to JavaScript not having access to the opened window. To mitigate this, you either need to always open the widgets
checkout in a new tab (see :ref:`Always open a new tab`) or set ``Cross-Origin-Opener-Policy: same-origin-allow-popups``
Working with Cross-Origin-Embedder-Policy Working with Cross-Origin-Embedder-Policy
----------------------------------------- -----------------------------------------
+17 -15
View File
@@ -22,7 +22,7 @@ classifiers = [
"Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.11",
"Framework :: Django :: 4.2", "Framework :: Django :: 4.1",
] ]
dependencies = [ dependencies = [
@@ -35,15 +35,16 @@ dependencies = [
"cryptography>=3.4.2", "cryptography>=3.4.2",
"css-inline==0.14.*", "css-inline==0.14.*",
"defusedcsv>=1.1.0", "defusedcsv>=1.1.0",
"Django[argon2]==4.2.*,>=4.2.15", "dj-static",
"Django[argon2]==4.2.*",
"django-bootstrap3==24.2", "django-bootstrap3==24.2",
"django-compressor==4.5.1", "django-compressor==4.5",
"django-countries==7.6.*", "django-countries==7.6.*",
"django-filter==24.3", "django-filter==24.2",
"django-formset-js-improved==0.5.0.3", "django-formset-js-improved==0.5.0.3",
"django-formtools==2.5.1", "django-formtools==2.5.1",
"django-hierarkey==1.2.*", "django-hierarkey==1.2.*",
"django-hijack==3.6.*", "django-hijack==3.5.*",
"django-i18nfield==1.9.*,>=1.9.4", "django-i18nfield==1.9.*,>=1.9.4",
"django-libsass==0.9", "django-libsass==0.9",
"django-localflavor==4.0", "django-localflavor==4.0",
@@ -61,10 +62,10 @@ dependencies = [
"importlib_metadata==8.*", # Polyfill, we can probably drop this once we require Python 3.10+ "importlib_metadata==8.*", # Polyfill, we can probably drop this once we require Python 3.10+
"isoweek", "isoweek",
"jsonschema", "jsonschema",
"kombu==5.4.*", "kombu==5.3.*",
"libsass==0.23.*", "libsass==0.23.*",
"lxml", "lxml",
"markdown==3.7", # 3.3.5 requires importlib-metadata>=4.4, but django-bootstrap3 requires importlib-metadata<3. "markdown==3.6", # 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 # We can upgrade markdown again once django-bootstrap3 upgrades or once we drop Python 3.6 and 3.7
"mt-940==4.30.*", "mt-940==4.30.*",
"oauthlib==3.2.*", "oauthlib==3.2.*",
@@ -72,17 +73,17 @@ dependencies = [
"packaging", "packaging",
"paypalrestsdk==1.13.*", "paypalrestsdk==1.13.*",
"paypal-checkout-serversdk==1.0.*", "paypal-checkout-serversdk==1.0.*",
"PyJWT==2.9.*", "PyJWT==2.8.*",
"phonenumberslite==8.13.*", "phonenumberslite==8.13.*",
"Pillow==10.4.*", "Pillow==10.4.*",
"pretix-plugin-build", "pretix-plugin-build",
"protobuf==5.28.*", "protobuf==5.27.*",
"psycopg2-binary", "psycopg2-binary",
"pycountry", "pycountry",
"pycparser==2.22", "pycparser==2.22",
"pycryptodome==3.20.*", "pycryptodome==3.20.*",
"pypdf==4.3.*", "pypdf==4.2.*",
"python-bidi==0.6.*", # Support for Arabic in reportlab "python-bidi==0.4.*", # Support for Arabic in reportlab
"python-dateutil==2.9.*", "python-dateutil==2.9.*",
"pytz", "pytz",
"pytz-deprecation-shim==0.1.*", "pytz-deprecation-shim==0.1.*",
@@ -91,9 +92,10 @@ dependencies = [
"redis==5.0.*", "redis==5.0.*",
"reportlab==4.2.*", "reportlab==4.2.*",
"requests==2.31.*", "requests==2.31.*",
"sentry-sdk==2.13.*", "sentry-sdk==2.5.*",
"sepaxml==2.6.*", "sepaxml==2.6.*",
"slimit", "slimit",
"static3==0.7.*",
"stripe==7.9.*", "stripe==7.9.*",
"text-unidecode==1.*", "text-unidecode==1.*",
"tlds>=2020041600", "tlds>=2020041600",
@@ -108,10 +110,10 @@ dependencies = [
[project.optional-dependencies] [project.optional-dependencies]
memcached = ["pylibmc"] memcached = ["pylibmc"]
dev = [ dev = [
"aiohttp==3.10.*", "aiohttp==3.9.*",
"coverage", "coverage",
"coveralls", "coveralls",
"fakeredis==2.24.*", "fakeredis==2.23.*",
"flake8==7.1.*", "flake8==7.1.*",
"freezegun", "freezegun",
"isort==5.13.*", "isort==5.13.*",
@@ -125,7 +127,7 @@ dev = [
"pytest-rerunfailures==14.*", "pytest-rerunfailures==14.*",
"pytest-sugar", "pytest-sugar",
"pytest-xdist==3.6.*", "pytest-xdist==3.6.*",
"pytest==8.3.*", "pytest==8.2.*",
"responses", "responses",
] ]
+1 -1
View File
@@ -19,4 +19,4 @@
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see # You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>. # <https://www.gnu.org/licenses/>.
# #
__version__ = "2024.9.0.dev0" __version__ = "2024.7.0.dev0"
-10
View File
@@ -62,7 +62,6 @@ INSTALLED_APPS = [
'pretix.plugins.badges', 'pretix.plugins.badges',
'pretix.plugins.manualpayment', 'pretix.plugins.manualpayment',
'pretix.plugins.returnurl', 'pretix.plugins.returnurl',
'pretix.plugins.autocheckin',
'pretix.plugins.webcheckin', 'pretix.plugins.webcheckin',
'django_countries', 'django_countries',
'oauth2_provider', 'oauth2_provider',
@@ -75,20 +74,11 @@ FORMAT_MODULE_PATH = [
'pretix.helpers.formats', 'pretix.helpers.formats',
] ]
CORE_MODULES = {
"pretix.base",
"pretix.presale",
"pretix.control",
"pretix.plugins.checkinlists",
"pretix.plugins.reports",
}
ALL_LANGUAGES = [ ALL_LANGUAGES = [
('en', _('English')), ('en', _('English')),
('de', _('German')), ('de', _('German')),
('de-informal', _('German (informal)')), ('de-informal', _('German (informal)')),
('ar', _('Arabic')), ('ar', _('Arabic')),
('eu', _('Basque')),
('ca', _('Catalan')), ('ca', _('Catalan')),
('zh-hans', _('Chinese (simplified)')), ('zh-hans', _('Chinese (simplified)')),
('zh-hant', _('Chinese (traditional)')), ('zh-hant', _('Chinese (traditional)')),
-82
View File
@@ -1,82 +0,0 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
from django import forms
from django.core.exceptions import ValidationError
from django.db.models import Q
from django.db.models.constants import LOOKUP_SEP
from django.forms import MultipleChoiceField
from django_filters import Filter
from django_filters.conf import settings
class MultipleCharField(forms.CharField):
widget = forms.MultipleHiddenInput
def to_python(self, value):
if not value:
return []
elif not isinstance(value, (list, tuple)):
raise ValidationError(
MultipleChoiceField.default_error_messages["invalid_list"], code="invalid_list"
)
return [str(val) for val in value]
class MultipleCharFilter(Filter):
"""
This filter performs OR(by default) or AND(using conjoined=True) query
on the selected inputs.
"""
field_class = MultipleCharField
def __init__(self, *args, **kwargs):
self.conjoined = kwargs.pop("conjoined", False)
super().__init__(*args, **kwargs)
def filter(self, qs, value):
if not value:
# Even though not a noop, no point filtering if empty.
return qs
if not self.conjoined:
q = Q()
for v in set(value):
predicate = self.get_filter_predicate(v)
if self.conjoined:
qs = self.get_method(qs)(**predicate)
else:
q |= Q(**predicate)
if not self.conjoined:
qs = self.get_method(qs)(q)
return qs.distinct() if self.distinct else qs
def get_filter_predicate(self, v):
name = self.field_name
if name and self.lookup_expr != settings.DEFAULT_LOOKUP_EXPR:
name = LOOKUP_SEP.join([name, self.lookup_expr])
try:
return {name: getattr(v, self.field.to_field_name)}
except (AttributeError, TypeError):
return {name: v}
-3
View File
@@ -21,7 +21,6 @@
# #
import json import json
from django.db.models import prefetch_related_objects
from rest_framework import serializers from rest_framework import serializers
from rest_framework.exceptions import ValidationError from rest_framework.exceptions import ValidationError
@@ -81,7 +80,6 @@ class SalesChannelMigrationMixin:
def to_internal_value(self, data): def to_internal_value(self, data):
if "sales_channels" in data: if "sales_channels" in data:
prefetch_related_objects([self.organizer], "sales_channels")
all_channels = { all_channels = {
s.identifier for s in s.identifier for s in
self.organizer.sales_channels.all() self.organizer.sales_channels.all()
@@ -111,7 +109,6 @@ class SalesChannelMigrationMixin:
def to_representation(self, value): def to_representation(self, value):
value = super().to_representation(value) value = super().to_representation(value)
if value.get("all_sales_channels"): if value.get("all_sales_channels"):
prefetch_related_objects([self.organizer], "sales_channels")
value["sales_channels"] = sorted([ value["sales_channels"] = sorted([
s.identifier for s in s.identifier for s in
self.organizer.sales_channels.all() self.organizer.sales_channels.all()
+2 -79
View File
@@ -35,7 +35,7 @@
import logging import logging
from django.conf import settings from django.conf import settings
from django.core.exceptions import PermissionDenied, ValidationError from django.core.exceptions import ValidationError
from django.db import transaction from django.db import transaction
from django.utils.crypto import get_random_string from django.utils.crypto import get_random_string
from django.utils.functional import cached_property from django.utils.functional import cached_property
@@ -52,8 +52,7 @@ from pretix.api.serializers import (
from pretix.api.serializers.i18n import I18nAwareModelSerializer from pretix.api.serializers.i18n import I18nAwareModelSerializer
from pretix.api.serializers.settings import SettingsSerializer from pretix.api.serializers.settings import SettingsSerializer
from pretix.base.models import ( from pretix.base.models import (
CartPosition, Device, Event, OrderPosition, SalesChannel, Seat, TaxRule, Device, Event, SalesChannel, TaxRule, TeamAPIToken,
TeamAPIToken, Voucher,
) )
from pretix.base.models.event import SubEvent from pretix.base.models.event import SubEvent
from pretix.base.models.items import ( from pretix.base.models.items import (
@@ -845,7 +844,6 @@ class EventSettingsSerializer(SettingsSerializer):
'reusable_media_type_nfc_mf0aes_autocreate_giftcard', 'reusable_media_type_nfc_mf0aes_autocreate_giftcard',
'reusable_media_type_nfc_mf0aes_autocreate_giftcard_currency', 'reusable_media_type_nfc_mf0aes_autocreate_giftcard_currency',
'reusable_media_type_nfc_mf0aes_random_uid', 'reusable_media_type_nfc_mf0aes_random_uid',
'seating_allow_blocked_seats_for_channel',
] ]
readonly_fields = [ readonly_fields = [
# These are read-only since they are currently only settable on organizers, not events # These are read-only since they are currently only settable on organizers, not events
@@ -896,7 +894,6 @@ class DeviceEventSettingsSerializer(EventSettingsSerializer):
'locale', 'locale',
'last_order_modification_date', 'last_order_modification_date',
'show_quota_left', 'show_quota_left',
'show_dates_on_frontpage',
'max_items_per_order', 'max_items_per_order',
'attendee_names_asked', 'attendee_names_asked',
'attendee_names_required', 'attendee_names_required',
@@ -972,77 +969,3 @@ class ItemMetaPropertiesSerializer(I18nAwareModelSerializer):
class Meta: class Meta:
model = ItemMetaProperty model = ItemMetaProperty
fields = ('id', 'name', 'default', 'required', 'allowed_values') fields = ('id', 'name', 'default', 'required', 'allowed_values')
def prefetch_by_id(items, qs, id_attr, target_attr):
"""
Prefetches a related object on each item in the given list of items by searching by id or another
unique field. The id value is read from the attribute on item specified in `id_attr`, searched on queryset `qs` by
the primary key, and the resulting prefetched model object is stored into `target_attr` on the item.
"""
ids = [getattr(item, id_attr) for item in items if getattr(item, id_attr)]
if ids:
result = qs.in_bulk(id_list=ids)
for item in items:
setattr(item, target_attr, result.get(getattr(item, id_attr)))
class SeatSerializer(I18nAwareModelSerializer):
orderposition = serializers.IntegerField(source='orderposition_id')
cartposition = serializers.IntegerField(source='cartposition_id')
voucher = serializers.IntegerField(source='voucher_id')
class Meta:
model = Seat
read_only_fields = (
'id', 'subevent', 'zone_name', 'row_name', 'row_label',
'seat_number', 'seat_label', 'seat_guid', 'product',
'orderposition', 'cartposition', 'voucher',
)
fields = (
'id', 'subevent', 'zone_name', 'row_name', 'row_label',
'seat_number', 'seat_label', 'seat_guid', 'product', 'blocked',
'orderposition', 'cartposition', 'voucher',
)
def prefetch_expanded_data(self, items, request, expand_fields):
if 'orderposition' in expand_fields:
if 'can_view_orders' not in request.eventpermset:
raise PermissionDenied('can_view_orders permission required for expand=orderposition')
prefetch_by_id(items, OrderPosition.objects.prefetch_related('order'), 'orderposition_id', 'orderposition')
if 'cartposition' in expand_fields:
if 'can_view_orders' not in request.eventpermset:
raise PermissionDenied('can_view_orders permission required for expand=cartposition')
prefetch_by_id(items, CartPosition.objects, 'cartposition_id', 'cartposition')
if 'voucher' in expand_fields:
if 'can_view_vouchers' not in request.eventpermset:
raise PermissionDenied('can_view_vouchers permission required for expand=voucher')
prefetch_by_id(items, Voucher.objects, 'voucher_id', 'voucher')
def __init__(self, instance, *args, **kwargs):
if not kwargs.get('data'):
self.prefetch_expanded_data(instance if hasattr(instance, '__iter__') else [instance],
kwargs['context']['request'],
kwargs['context']['expand_fields'])
super().__init__(instance, *args, **kwargs)
if 'orderposition' in self.context['expand_fields']:
from pretix.api.serializers.media import (
NestedOrderPositionSerializer,
)
self.fields['orderposition'] = NestedOrderPositionSerializer(read_only=True, context=self.context['order_context'])
try:
del self.fields['orderposition'].fields['seat']
except KeyError:
pass
if 'cartposition' in self.context['expand_fields']:
from pretix.api.serializers.cart import CartPositionSerializer
self.fields['cartposition'] = CartPositionSerializer(read_only=True)
del self.fields['cartposition'].fields['seat']
if 'voucher' in self.context['expand_fields']:
from pretix.api.serializers.voucher import VoucherSerializer
self.fields['voucher'] = VoucherSerializer(read_only=True)
del self.fields['voucher'].fields['seat']
+4 -10
View File
@@ -273,15 +273,9 @@ class AnswerSerializer(I18nAwareModelSerializer):
class CheckinSerializer(I18nAwareModelSerializer): class CheckinSerializer(I18nAwareModelSerializer):
device_id = serializers.SlugRelatedField(
source='device',
slug_field='device_id',
read_only=True,
)
class Meta: class Meta:
model = Checkin model = Checkin
fields = ('id', 'datetime', 'list', 'auto_checked_in', 'gate', 'device', 'device_id', 'type') fields = ('id', 'datetime', 'list', 'auto_checked_in', 'gate', 'device', 'type')
class FailedCheckinSerializer(I18nAwareModelSerializer): class FailedCheckinSerializer(I18nAwareModelSerializer):
@@ -732,7 +726,7 @@ class OrderSerializer(I18nAwareModelSerializer):
'code', 'event', 'status', 'testmode', 'secret', 'email', 'phone', 'locale', 'datetime', 'expires', 'payment_date', 'code', 'event', 'status', 'testmode', 'secret', 'email', 'phone', 'locale', 'datetime', 'expires', 'payment_date',
'payment_provider', 'fees', 'total', 'comment', 'custom_followup_at', 'invoice_address', 'positions', 'downloads', 'payment_provider', 'fees', 'total', 'comment', 'custom_followup_at', 'invoice_address', 'positions', 'downloads',
'checkin_attention', 'checkin_text', 'last_modified', 'payments', 'refunds', 'require_approval', 'sales_channel', 'checkin_attention', 'checkin_text', 'last_modified', 'payments', 'refunds', 'require_approval', 'sales_channel',
'url', 'customer', 'valid_if_pending', 'api_meta' 'url', 'customer', 'valid_if_pending'
) )
read_only_fields = ( read_only_fields = (
'code', 'status', 'testmode', 'secret', 'datetime', 'expires', 'payment_date', 'code', 'status', 'testmode', 'secret', 'datetime', 'expires', 'payment_date',
@@ -792,7 +786,7 @@ class OrderSerializer(I18nAwareModelSerializer):
# Even though all fields that shouldn't be edited are marked as read_only in the serializer # Even though all fields that shouldn't be edited are marked as read_only in the serializer
# (hopefully), we'll be extra careful here and be explicit about the model fields we update. # (hopefully), we'll be extra careful here and be explicit about the model fields we update.
update_fields = ['comment', 'custom_followup_at', 'checkin_attention', 'checkin_text', 'email', 'locale', update_fields = ['comment', 'custom_followup_at', 'checkin_attention', 'checkin_text', 'email', 'locale',
'phone', 'valid_if_pending', 'api_meta'] 'phone', 'valid_if_pending']
if 'invoice_address' in validated_data: if 'invoice_address' in validated_data:
iadata = validated_data.pop('invoice_address') iadata = validated_data.pop('invoice_address')
@@ -1065,7 +1059,7 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
fields = ('code', 'status', 'testmode', 'email', 'phone', 'locale', 'payment_provider', 'fees', 'comment', 'sales_channel', fields = ('code', 'status', 'testmode', 'email', 'phone', 'locale', 'payment_provider', 'fees', 'comment', 'sales_channel',
'invoice_address', 'positions', 'checkin_attention', 'checkin_text', 'payment_info', 'payment_date', 'invoice_address', 'positions', 'checkin_attention', 'checkin_text', 'payment_info', 'payment_date',
'consume_carts', 'force', 'send_email', 'simulate', 'customer', 'custom_followup_at', 'consume_carts', 'force', 'send_email', 'simulate', 'customer', 'custom_followup_at',
'require_approval', 'valid_if_pending', 'expires', 'api_meta') 'require_approval', 'valid_if_pending', 'expires')
def validate_payment_provider(self, pp): def validate_payment_provider(self, pp):
if pp is None: if pp is None:
-5
View File
@@ -87,7 +87,6 @@ event_router.register(r'invoices', order.InvoiceViewSet)
event_router.register(r'revokedsecrets', order.RevokedSecretViewSet, basename='revokedsecrets') event_router.register(r'revokedsecrets', order.RevokedSecretViewSet, basename='revokedsecrets')
event_router.register(r'blockedsecrets', order.BlockedSecretViewSet, basename='blockedsecrets') event_router.register(r'blockedsecrets', order.BlockedSecretViewSet, basename='blockedsecrets')
event_router.register(r'taxrules', event.TaxRuleViewSet) event_router.register(r'taxrules', event.TaxRuleViewSet)
event_router.register(r'seats', event.SeatViewSet)
event_router.register(r'waitinglistentries', waitinglist.WaitingListViewSet) event_router.register(r'waitinglistentries', waitinglist.WaitingListViewSet)
event_router.register(r'checkinlists', checkin.CheckinListViewSet) event_router.register(r'checkinlists', checkin.CheckinListViewSet)
event_router.register(r'cartpositions', cart.CartPositionViewSet) event_router.register(r'cartpositions', cart.CartPositionViewSet)
@@ -96,9 +95,6 @@ event_router.register(r'exporters', exporters.EventExportersViewSet, basename='e
event_router.register(r'shredders', shredders.EventShreddersViewSet, basename='shredders') event_router.register(r'shredders', shredders.EventShreddersViewSet, basename='shredders')
event_router.register(r'item_meta_properties', event.ItemMetaPropertiesViewSet) event_router.register(r'item_meta_properties', event.ItemMetaPropertiesViewSet)
subevent_router = routers.DefaultRouter()
subevent_router.register(r'seats', event.SeatViewSet)
checkinlist_router = routers.DefaultRouter() checkinlist_router = routers.DefaultRouter()
checkinlist_router.register(r'positions', checkin.CheckinListPositionViewSet, basename='checkinlistpos') checkinlist_router.register(r'positions', checkin.CheckinListPositionViewSet, basename='checkinlistpos')
@@ -136,7 +132,6 @@ urlpatterns = [
re_path(r'^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/settings/$', event.EventSettingsView.as_view(), re_path(r'^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/settings/$', event.EventSettingsView.as_view(),
name="event.settings"), name="event.settings"),
re_path(r'^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/', include(event_router.urls)), re_path(r'^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/', include(event_router.urls)),
re_path(r'^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/subevents/(?P<subevent>\d+)/', include(subevent_router.urls)),
re_path(r'^organizers/(?P<organizer>[^/]+)/teams/(?P<team>[^/]+)/', include(team_router.urls)), re_path(r'^organizers/(?P<organizer>[^/]+)/teams/(?P<team>[^/]+)/', include(team_router.urls)),
re_path(r'^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/items/(?P<item>[^/]+)/', include(item_router.urls)), re_path(r'^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/items/(?P<item>[^/]+)/', include(item_router.urls)),
re_path(r'^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/questions/(?P<question>[^/]+)/', re_path(r'^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/questions/(?P<question>[^/]+)/',
+1 -1
View File
@@ -377,7 +377,7 @@ def _checkin_list_position_queryset(checkinlists, ignore_status=False, ignore_pr
Prefetch( Prefetch(
'positions', 'positions',
OrderPosition.objects.prefetch_related( OrderPosition.objects.prefetch_related(
Prefetch('checkins', queryset=Checkin.objects.select_related('device')), Prefetch('checkins', queryset=Checkin.objects.all()),
'item', 'variation', 'answers', 'answers__options', 'answers__question', 'item', 'variation', 'answers', 'answers__options', 'answers__question',
) )
) )
+5 -81
View File
@@ -40,9 +40,7 @@ from django.utils.timezone import now
from django_filters.rest_framework import DjangoFilterBackend, FilterSet from django_filters.rest_framework import DjangoFilterBackend, FilterSet
from django_scopes import scopes_disabled from django_scopes import scopes_disabled
from rest_framework import serializers, views, viewsets from rest_framework import serializers, views, viewsets
from rest_framework.exceptions import ( from rest_framework.exceptions import PermissionDenied, ValidationError
NotFound, PermissionDenied, ValidationError,
)
from rest_framework.generics import get_object_or_404 from rest_framework.generics import get_object_or_404
from rest_framework.response import Response from rest_framework.response import Response
@@ -50,12 +48,12 @@ from pretix.api.auth.permission import EventCRUDPermission
from pretix.api.pagination import TotalOrderingFilter from pretix.api.pagination import TotalOrderingFilter
from pretix.api.serializers.event import ( from pretix.api.serializers.event import (
CloneEventSerializer, DeviceEventSettingsSerializer, EventSerializer, CloneEventSerializer, DeviceEventSettingsSerializer, EventSerializer,
EventSettingsSerializer, ItemMetaPropertiesSerializer, SeatSerializer, EventSettingsSerializer, ItemMetaPropertiesSerializer, SubEventSerializer,
SubEventSerializer, TaxRuleSerializer, TaxRuleSerializer,
) )
from pretix.api.views import ConditionalListView from pretix.api.views import ConditionalListView
from pretix.base.models import ( from pretix.base.models import (
CartPosition, Device, Event, ItemMetaProperty, Seat, SeatCategoryMapping, CartPosition, Device, Event, ItemMetaProperty, SeatCategoryMapping,
TaxRule, TeamAPIToken, TaxRule, TeamAPIToken,
) )
from pretix.base.models.event import SubEvent from pretix.base.models.event import SubEvent
@@ -445,7 +443,7 @@ class SubEventViewSet(ConditionalListView, viewsets.ModelViewSet):
elif self.request.user.is_authenticated: elif self.request.user.is_authenticated:
qs = SubEvent.objects.filter( qs = SubEvent.objects.filter(
event__organizer=self.request.organizer, event__organizer=self.request.organizer,
event__in=self.request.user.get_events_with_any_permission(request=self.request) event__in=self.request.user.get_events_with_any_permission()
) )
qs = filter_qs_by_attr(qs, self.request) qs = filter_qs_by_attr(qs, self.request)
@@ -669,77 +667,3 @@ class EventSettingsView(views.APIView):
'request': request 'request': request
}) })
return Response(s.data) return Response(s.data)
class SeatFilter(FilterSet):
is_available = django_filters.BooleanFilter(method="is_available_qs")
def is_available_qs(self, queryset, name, value):
expr = (
Q(orderposition_id__isnull=True, cartposition_id__isnull=True, voucher_id__isnull=True)
)
if self.request.event.settings.seating_minimal_distance:
expr = expr & Q(has_closeby_taken=False)
if value:
return queryset.filter(expr)
else:
return queryset.exclude(expr)
class Meta:
model = Seat
fields = ('zone_name', 'row_name', 'row_label', 'seat_number', 'seat_label', 'seat_guid', 'blocked',)
class SeatViewSet(ConditionalListView, viewsets.ModelViewSet):
serializer_class = SeatSerializer
queryset = Seat.objects.none()
write_permission = 'can_change_event_settings'
filter_backends = (DjangoFilterBackend, )
filterset_class = SeatFilter
def get_queryset(self):
if self.request.event.has_subevents and 'subevent' in self.request.resolver_match.kwargs:
try:
subevent = self.request.event.subevents.get(pk=self.request.resolver_match.kwargs['subevent'])
except SubEvent.DoesNotExist:
raise NotFound('Subevent not found')
qs = Seat.annotated(
event_id=self.request.event.id,
subevent=subevent,
qs=subevent.seats.all(),
annotate_ids=True,
minimal_distance=self.request.event.settings.seating_minimal_distance,
distance_only_within_row=self.request.event.settings.seating_distance_only_within_row,
)
elif not self.request.event.has_subevents and 'subevent' not in self.request.resolver_match.kwargs:
qs = Seat.annotated(
event_id=self.request.event.id,
subevent=None,
qs=self.request.event.seats.all(),
annotate_ids=True,
minimal_distance=self.request.event.settings.seating_minimal_distance,
distance_only_within_row=self.request.event.settings.seating_distance_only_within_row,
)
else:
raise NotFound('Please use the subevent-specific endpoint' if self.request.event.has_subevents
else 'This event has no subevents')
return qs
def get_serializer_context(self):
ctx = super().get_serializer_context()
ctx['expand_fields'] = self.request.query_params.getlist('expand')
ctx['order_context'] = {
'event': self.request.event,
'pdf_data': None,
}
return ctx
def perform_update(self, serializer):
super().perform_update(serializer)
serializer.instance.event.log_action(
"pretix.event.seats.blocks.changed",
user=self.request.user,
auth=self.request.auth,
data={"seats": [serializer.instance.pk]},
)
+1 -1
View File
@@ -78,7 +78,7 @@ class ReusableMediaViewSet(viewsets.ModelViewSet):
queryset=OrderPosition.objects.select_related( queryset=OrderPosition.objects.select_related(
'order', 'order__event', 'order__event__organizer', 'seat', 'order', 'order__event', 'order__event__organizer', 'seat',
).prefetch_related( ).prefetch_related(
Prefetch('checkins', queryset=Checkin.objects.select_related('device')), Prefetch('checkins', queryset=Checkin.objects.all()),
'answers', 'answers__options', 'answers__question', 'answers', 'answers__options', 'answers__question',
) )
), ),
+10 -8
View File
@@ -49,7 +49,6 @@ from rest_framework.mixins import CreateModelMixin
from rest_framework.permissions import SAFE_METHODS from rest_framework.permissions import SAFE_METHODS
from rest_framework.response import Response from rest_framework.response import Response
from pretix.api.filters import MultipleCharFilter
from pretix.api.models import OAuthAccessToken from pretix.api.models import OAuthAccessToken
from pretix.api.pagination import TotalOrderingFilter from pretix.api.pagination import TotalOrderingFilter
from pretix.api.serializers.order import ( from pretix.api.serializers.order import (
@@ -258,7 +257,7 @@ class OrderViewSetMixin:
return Prefetch( return Prefetch(
'positions', 'positions',
opq.all().prefetch_related( opq.all().prefetch_related(
Prefetch('checkins', queryset=Checkin.objects.select_related('device')), Prefetch('checkins', queryset=Checkin.objects.all()),
Prefetch('item', queryset=self.request.event.items.prefetch_related( Prefetch('item', queryset=self.request.event.items.prefetch_related(
Prefetch('meta_values', ItemMetaValue.objects.select_related('property'), to_attr='meta_values_cached') Prefetch('meta_values', ItemMetaValue.objects.select_related('property'), to_attr='meta_values_cached')
)), )),
@@ -279,7 +278,7 @@ class OrderViewSetMixin:
return Prefetch( return Prefetch(
'positions', 'positions',
opq.all().prefetch_related( opq.all().prefetch_related(
Prefetch('checkins', queryset=Checkin.objects.select_related('device')), Prefetch('checkins', queryset=Checkin.objects.all()),
'item', 'variation', 'item', 'variation',
Prefetch('answers', queryset=QuestionAnswer.objects.prefetch_related('options', 'question').order_by('question__position')), Prefetch('answers', queryset=QuestionAnswer.objects.prefetch_related('options', 'question').order_by('question__position')),
'seat', 'seat',
@@ -1092,7 +1091,7 @@ class OrderPositionViewSet(viewsets.ModelViewSet):
'item_meta_properties', 'item_meta_properties',
) )
qs = qs.prefetch_related( qs = qs.prefetch_related(
Prefetch('checkins', queryset=Checkin.objects.select_related("device")), Prefetch('checkins', queryset=Checkin.objects.all()),
Prefetch('item', queryset=self.request.event.items.prefetch_related( Prefetch('item', queryset=self.request.event.items.prefetch_related(
Prefetch('meta_values', ItemMetaValue.objects.select_related('property'), Prefetch('meta_values', ItemMetaValue.objects.select_related('property'),
to_attr='meta_values_cached') to_attr='meta_values_cached')
@@ -1111,7 +1110,7 @@ class OrderPositionViewSet(viewsets.ModelViewSet):
Prefetch( Prefetch(
'positions', 'positions',
qs.prefetch_related( qs.prefetch_related(
Prefetch('checkins', queryset=Checkin.objects.select_related('device')), Prefetch('checkins', queryset=Checkin.objects.all()),
Prefetch('item', queryset=self.request.event.items.prefetch_related( Prefetch('item', queryset=self.request.event.items.prefetch_related(
Prefetch('meta_values', ItemMetaValue.objects.select_related('property'), Prefetch('meta_values', ItemMetaValue.objects.select_related('property'),
to_attr='meta_values_cached') to_attr='meta_values_cached')
@@ -1135,7 +1134,7 @@ class OrderPositionViewSet(viewsets.ModelViewSet):
) )
else: else:
qs = qs.prefetch_related( qs = qs.prefetch_related(
Prefetch('checkins', queryset=Checkin.objects.select_related("device")), Prefetch('checkins', queryset=Checkin.objects.all()),
'answers', 'answers__options', 'answers__question', 'answers', 'answers__options', 'answers__question',
).select_related( ).select_related(
'item', 'order', 'order__event', 'order__event__organizer', 'seat' 'item', 'order', 'order__event', 'order__event__organizer', 'seat'
@@ -1826,14 +1825,17 @@ class RefundViewSet(CreateModelMixin, viewsets.ReadOnlyModelViewSet):
with scopes_disabled(): with scopes_disabled():
class InvoiceFilter(FilterSet): class InvoiceFilter(FilterSet):
refers = django_filters.CharFilter(method='refers_qs') refers = django_filters.CharFilter(method='refers_qs')
number = MultipleCharFilter(field_name='nr', lookup_expr='iexact') number = django_filters.CharFilter(method='nr_qs')
order = MultipleCharFilter(field_name='order', lookup_expr='code__iexact') order = django_filters.CharFilter(field_name='order', lookup_expr='code__iexact')
def refers_qs(self, queryset, name, value): def refers_qs(self, queryset, name, value):
return queryset.annotate( return queryset.annotate(
refers_nr=Concat('refers__prefix', 'refers__invoice_no') refers_nr=Concat('refers__prefix', 'refers__invoice_no')
).filter(refers_nr__iexact=value) ).filter(refers_nr__iexact=value)
def nr_qs(self, queryset, name, value):
return queryset.filter(nr__iexact=value)
class Meta: class Meta:
model = Invoice model = Invoice
fields = ['order', 'number', 'is_cancellation', 'refers', 'locale'] fields = ['order', 'number', 'is_cancellation', 'refers', 'locale']
-8
View File
@@ -19,8 +19,6 @@
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see # You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>. # <https://www.gnu.org/licenses/>.
# #
import django_filters
from django_filters.rest_framework import DjangoFilterBackend, FilterSet
from rest_framework import viewsets from rest_framework import viewsets
from pretix.api.models import WebHook from pretix.api.models import WebHook
@@ -28,17 +26,11 @@ from pretix.api.serializers.webhooks import WebHookSerializer
from pretix.helpers.dicts import merge_dicts from pretix.helpers.dicts import merge_dicts
class WebhookFilter(FilterSet):
enabled = django_filters.rest_framework.BooleanFilter()
class WebHookViewSet(viewsets.ModelViewSet): class WebHookViewSet(viewsets.ModelViewSet):
serializer_class = WebHookSerializer serializer_class = WebHookSerializer
queryset = WebHook.objects.none() queryset = WebHook.objects.none()
permission = 'can_change_organizer_settings' permission = 'can_change_organizer_settings'
write_permission = 'can_change_organizer_settings' write_permission = 'can_change_organizer_settings'
filter_backends = (DjangoFilterBackend,)
filterset_class = WebhookFilter
def get_queryset(self): def get_queryset(self):
return self.request.organizer.webhooks.prefetch_related('listeners') return self.request.organizer.webhooks.prefetch_related('listeners')
-15
View File
@@ -126,17 +126,6 @@ class ParametrizedOrderWebhookEvent(ParametrizedWebhookEvent):
} }
class DeletedOrderWebhookEvent(ParametrizedWebhookEvent):
def build_payload(self, logentry: LogEntry):
return {
'notification_id': logentry.pk,
'organizer': logentry.organizer.slug,
'event': logentry.event.slug,
'code': logentry.parsed_data.get("code"),
'action': logentry.action_type,
}
class ParametrizedEventWebhookEvent(ParametrizedWebhookEvent): class ParametrizedEventWebhookEvent(ParametrizedWebhookEvent):
def build_payload(self, logentry: LogEntry): def build_payload(self, logentry: LogEntry):
@@ -308,10 +297,6 @@ def register_default_webhook_events(sender, **kwargs):
'pretix.event.order.denied', 'pretix.event.order.denied',
_('Order denied'), _('Order denied'),
), ),
DeletedOrderWebhookEvent(
'pretix.event.order.deleted',
_('Order deleted'),
),
ParametrizedOrderPositionCheckinWebhookEvent( ParametrizedOrderPositionCheckinWebhookEvent(
'pretix.event.checkin', 'pretix.event.checkin',
_('Ticket checked in'), _('Ticket checked in'),
+4 -7
View File
@@ -207,13 +207,10 @@ class ListExporter(BaseExporter):
def get_filename(self): def get_filename(self):
return 'export' return 'export'
def get_csv_encoding(self):
return 'utf-8'
def _render_csv(self, form_data, output_file=None, **kwargs): def _render_csv(self, form_data, output_file=None, **kwargs):
if output_file: if output_file:
if 'b' in output_file.mode: if 'b' in output_file.mode:
output_file = io.TextIOWrapper(output_file, encoding=self.get_csv_encoding(), errors='replace', newline='') output_file = io.TextIOWrapper(output_file, encoding='utf-8', newline='')
writer = csv.writer(output_file, **kwargs) writer = csv.writer(output_file, **kwargs)
total = 0 total = 0
counter = 0 counter = 0
@@ -249,7 +246,7 @@ class ListExporter(BaseExporter):
if counter % max(10, total // 100) == 0: if counter % max(10, total // 100) == 0:
self.progress_callback(counter / total * 100) self.progress_callback(counter / total * 100)
writer.writerow(line) writer.writerow(line)
return self.get_filename() + '.csv', 'text/csv', output.getvalue().encode(self.get_csv_encoding(), errors='replace') return self.get_filename() + '.csv', 'text/csv', output.getvalue().encode("utf-8")
def prepare_xlsx_sheet(self, ws): def prepare_xlsx_sheet(self, ws):
pass pass
@@ -259,7 +256,7 @@ class ListExporter(BaseExporter):
ws = wb.create_sheet() ws = wb.create_sheet()
self.prepare_xlsx_sheet(ws) self.prepare_xlsx_sheet(ws)
try: try:
ws.title = str(self.verbose_name)[:30] ws.title = str(self.verbose_name)
except: except:
pass pass
total = 0 total = 0
@@ -377,7 +374,7 @@ class MultiSheetListExporter(ListExporter):
wb = SafeWorkbook(write_only=True) wb = SafeWorkbook(write_only=True)
n_sheets = len(self.sheets) n_sheets = len(self.sheets)
for i_sheet, (s, l) in enumerate(self.sheets): for i_sheet, (s, l) in enumerate(self.sheets):
ws = wb.create_sheet(str(l)[:30]) ws = wb.create_sheet(str(l))
if hasattr(self, 'prepare_xlsx_sheet_' + s): if hasattr(self, 'prepare_xlsx_sheet_' + s):
getattr(self, 'prepare_xlsx_sheet_' + s)(ws) getattr(self, 'prepare_xlsx_sheet_' + s)(ws)
+2 -5
View File
@@ -560,7 +560,7 @@ class OrderListExporter(MultiSheetListExporter):
), ),
).select_related( ).select_related(
'order', 'order__invoice_address', 'order__customer', 'item', 'variation', 'order', 'order__invoice_address', 'order__customer', 'item', 'variation',
'voucher', 'tax_rule', 'addon_to', 'voucher', 'tax_rule'
).prefetch_related( ).prefetch_related(
'subevent', 'subevent__meta_values', 'subevent', 'subevent__meta_values',
'answers', 'answers__question', 'answers__options' 'answers', 'answers__question', 'answers__options'
@@ -619,7 +619,6 @@ class OrderListExporter(MultiSheetListExporter):
_('Valid until'), _('Valid until'),
_('Order comment'), _('Order comment'),
_('Follow-up date'), _('Follow-up date'),
_('Add-on to position ID'),
] ]
questions = list(Question.objects.filter(event__in=self.events)) questions = list(Question.objects.filter(event__in=self.events))
@@ -653,8 +652,7 @@ class OrderListExporter(MultiSheetListExporter):
_('VAT ID'), _('VAT ID'),
] ]
headers += [ headers += [
_('Sales channel'), _('Sales channel'), _('Order locale'),
_('Order locale'),
_('E-mail address verified'), _('E-mail address verified'),
_('External customer ID'), _('External customer ID'),
_('Check-in lists'), _('Check-in lists'),
@@ -745,7 +743,6 @@ class OrderListExporter(MultiSheetListExporter):
] ]
row.append(order.comment) row.append(order.comment)
row.append(order.custom_followup_at.strftime("%Y-%m-%d") if order.custom_followup_at else "") row.append(order.custom_followup_at.strftime("%Y-%m-%d") if order.custom_followup_at else "")
row.append(op.addon_to.positionid if op.addon_to_id else "")
acache = {} acache = {}
for a in op.answers.all(): for a in op.answers.all():
# We do not want to localize Date, Time and Datetime question answers, as those can lead # We do not want to localize Date, Time and Datetime question answers, as those can lead
+1 -2
View File
@@ -38,7 +38,6 @@ from datetime import datetime
from django import forms from django import forms
from django.utils.formats import get_format from django.utils.formats import get_format
from django.utils.functional import lazy from django.utils.functional import lazy
from django.utils.html import escape
from django.utils.timezone import get_current_timezone, now from django.utils.timezone import get_current_timezone, now
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@@ -65,7 +64,7 @@ def format_placeholders_help_text(placeholders, event=None):
placeholders = [(k, v.render_sample(event) if event else v) for k, v in placeholders.items()] placeholders = [(k, v.render_sample(event) if event else v) for k, v in placeholders.items()]
placeholders.sort(key=lambda x: x[0]) placeholders.sort(key=lambda x: x[0])
phs = [ phs = [
'<button type="button" class="content-placeholder" title="%s">{%s}</button>' % (escape(_("Sample: %s") % v) if v else "", escape(k)) '<button type="button" class="content-placeholder" title="%s">{%s}</button>' % (_("Sample: %s") % v if v else "", k)
for k, v in placeholders for k, v in placeholders
] ]
return _('Available placeholders: {list}').format( return _('Available placeholders: {list}').format(
+1 -1
View File
@@ -30,7 +30,7 @@ from typing import Tuple
import bleach import bleach
import vat_moss.exchange_rates import vat_moss.exchange_rates
from bidi import get_display from bidi.algorithm import get_display
from django.contrib.staticfiles import finders from django.contrib.staticfiles import finders
from django.db.models import Sum from django.db.models import Sum
from django.dispatch import receiver from django.dispatch import receiver
-164
View File
@@ -1,164 +0,0 @@
from collections import defaultdict
from django.urls import reverse
from django.utils.html import escape
from django.utils.translation import gettext_lazy as _, pgettext_lazy
from pretix.base.signals import PluginRegistry
def make_link(a_map, wrapper, is_active=True, event=None, plugin_name=None):
if a_map:
if is_active:
a_map['val'] = '<a href="{href}">{val}</a>'.format_map(a_map)
elif event and plugin_name:
a_map['val'] = (
'<i>{val}</i> <a href="{plugin_href}">'
'<span data-toggle="tooltip" title="{errmes}" class="fa fa-warning fa-fw"></span></a>'
).format_map({
**a_map,
"errmes": _("The relevant plugin is currently not active. To activate it, click here to go to the plugin settings."),
"plugin_href": reverse('control:event.settings.plugins', kwargs={
'organizer': event.organizer.slug,
'event': event.slug,
}) + '#plugin_' + plugin_name,
})
else:
a_map['val'] = '<i>{val}</i> <span data-toggle="tooltip" title="{errmes}" class="fa fa-warning fa-fw"></span>'.format_map({
**a_map,
"errmes": _("The relevant plugin is currently not active."),
})
return wrapper.format_map(a_map)
class LogEntryTypeRegistry(PluginRegistry):
def new_from_dict(self, data):
def reg(clz):
for action_type, plain in data.items():
self.register(clz(action_type=action_type, plain=plain))
return reg
log_entry_types = LogEntryTypeRegistry({'action_type': lambda o: getattr(o, 'action_type')})
class LogEntryType:
def __init__(self, action_type=None, plain=None):
assert self.__module__ != LogEntryType.__module__ # must not instantiate base classes, only derived ones
if action_type:
self.action_type = action_type
if plain:
self.plain = plain
def display(self, logentry):
if hasattr(self, 'plain'):
plain = str(self.plain)
if '{' in plain:
data = defaultdict(lambda: '?', logentry.parsed_data)
return plain.format_map(data)
else:
return plain
def get_object_link_info(self, logentry) -> dict:
pass
def get_object_link(self, logentry):
a_map = self.get_object_link_info(logentry)
return make_link(a_map, self.object_link_wrapper)
object_link_wrapper = '{val}'
def shred_pii(self, logentry):
raise NotImplementedError
class EventLogEntryType(LogEntryType):
def get_object_link_info(self, logentry) -> dict:
if hasattr(self, 'object_link_viewname') and hasattr(self, 'object_link_argname') and logentry.content_object:
return {
'href': reverse(self.object_link_viewname, kwargs={
'event': logentry.event.slug,
'organizer': logentry.event.organizer.slug,
self.object_link_argname: self.object_link_argvalue(logentry.content_object),
}),
'val': escape(self.object_link_display_name(logentry.content_object)),
}
def object_link_argvalue(self, content_object):
return content_object.id
def object_link_display_name(self, content_object):
return str(content_object)
class OrderLogEntryType(EventLogEntryType):
object_link_wrapper = _('Order {val}')
object_link_viewname = 'control:event.order'
object_link_argname = 'code'
def object_link_argvalue(self, order):
return order.code
def object_link_display_name(self, order):
return order.code
class VoucherLogEntryType(EventLogEntryType):
object_link_wrapper = _('Voucher {val}')
object_link_viewname = 'control:event.voucher'
object_link_argname = 'voucher'
def object_link_display_name(self, order):
return order.code[:6]
class ItemLogEntryType(EventLogEntryType):
object_link_wrapper = _('Product {val}')
object_link_viewname = 'control:event.item'
object_link_argname = 'item'
class SubEventLogEntryType(EventLogEntryType):
object_link_wrapper = pgettext_lazy('subevent', 'Date {val}')
object_link_viewname = 'control:event.subevent'
object_link_argname = 'subevent'
class QuotaLogEntryType(EventLogEntryType):
object_link_wrapper = _('Quota {val}')
object_link_viewname = 'control:event.items.quotas.show'
object_link_argname = 'quota'
class DiscountLogEntryType(EventLogEntryType):
object_link_wrapper = _('Discount {val}')
object_link_viewname = 'control:event.items.discounts.edit'
object_link_argname = 'discount'
class ItemCategoryLogEntryType(EventLogEntryType):
object_link_wrapper = _('Category {val}')
object_link_viewname = 'control:event.items.categories.edit'
object_link_argname = 'category'
class QuestionLogEntryType(EventLogEntryType):
object_link_wrapper = _('Question {val}')
object_link_viewname = 'control:event.items.questions.show'
object_link_argname = 'question'
class TaxRuleLogEntryType(EventLogEntryType):
object_link_wrapper = _('Tax rule {val}')
object_link_viewname = 'control:event.settings.tax.edit'
object_link_argname = 'rule'
class NoOpShredderMixin:
def shred_pii(self, logentry):
pass
class ClearDataShredderMixin:
def shred_pii(self, logentry):
logentry.data = None
@@ -1,18 +0,0 @@
# Generated by Django 4.2.13 on 2024-07-17 14:03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0268_remove_subevent_items_remove_subevent_variations_and_more'),
]
operations = [
migrations.AddField(
model_name='order',
name='api_meta',
field=models.JSONField(default=dict),
),
]
+4 -4
View File
@@ -28,9 +28,9 @@ from django.utils.translation import gettext as _, gettext_lazy, pgettext_lazy
from pretix.base.modelimport import ( from pretix.base.modelimport import (
BooleanColumnMixin, DatetimeColumnMixin, DecimalColumnMixin, ImportColumn, BooleanColumnMixin, DatetimeColumnMixin, DecimalColumnMixin, ImportColumn,
IntegerColumnMixin, SubeventColumnMixin, i18n_flat, IntegerColumnMixin, i18n_flat,
) )
from pretix.base.models import ItemVariation, Quota, Seat, SubEvent, Voucher from pretix.base.models import ItemVariation, Quota, Seat, Voucher
from pretix.base.signals import voucher_import_columns from pretix.base.signals import voucher_import_columns
@@ -55,11 +55,11 @@ class CodeColumn(ImportColumn):
obj.code = value obj.code = value
class SubeventColumn(SubeventColumnMixin, ImportColumn): class SubeventColumn(ImportColumn):
identifier = 'subevent' identifier = 'subevent'
verbose_name = pgettext_lazy('subevents', 'Date') verbose_name = pgettext_lazy('subevents', 'Date')
def assign(self, value, obj: SubEvent, **kwargs): def assign(self, value, obj: Voucher, **kwargs):
obj.subevent = value obj.subevent = value
+3 -3
View File
@@ -102,9 +102,9 @@ class CheckinList(LoggedModel):
auto_checkin_sales_channels = models.ManyToManyField( auto_checkin_sales_channels = models.ManyToManyField(
"SalesChannel", "SalesChannel",
verbose_name=_('Sales channels to automatically check in'), verbose_name=_('Sales channels to automatically check in'),
help_text=_('This option is deprecated and will be removed in the next months. As a replacement, our new plugin ' help_text=_('All items on this check-in list will be automatically marked as checked-in when purchased through '
'"Auto check-in" can be used. When we remove this option, we will automatically migrate your event ' 'any of the selected sales channels. This option can be useful when tickets sold at the box office '
'to use the new plugin.'), 'are not checked again before entry and should be considered validated directly upon purchase.'),
blank=True, blank=True,
) )
rules = models.JSONField(default=dict, blank=True) rules = models.JSONField(default=dict, blank=True)
+11 -6
View File
@@ -60,6 +60,7 @@ from django.urls import reverse
from django.utils.crypto import get_random_string from django.utils.crypto import get_random_string
from django.utils.formats import date_format from django.utils.formats import date_format
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.html import format_html
from django.utils.timezone import make_aware, now from django.utils.timezone import make_aware, now
from django.utils.translation import gettext, gettext_lazy as _ from django.utils.translation import gettext, gettext_lazy as _
from django_scopes import ScopedManager, scopes_disabled from django_scopes import ScopedManager, scopes_disabled
@@ -179,10 +180,14 @@ class EventMixin:
""" """
tz = tz or self.timezone tz = tz or self.timezone
if (not self.settings.show_date_to and not force_show_end) or not self.date_to: if (not self.settings.show_date_to and not force_show_end) or not self.date_to:
df, dt = self.date_from, self.date_from if as_html:
else: return format_html(
df, dt = self.date_from, self.date_to "<time datetime=\"{}\">{}</time>",
return daterange(df.astimezone(tz), dt.astimezone(tz), as_html) _date(self.date_from.astimezone(tz), "Y-m-d"),
_date(self.date_from.astimezone(tz), "DATE_FORMAT"),
)
return _date(self.date_from.astimezone(tz), "DATE_FORMAT")
return daterange(self.date_from.astimezone(tz), self.date_to.astimezone(tz), as_html)
def get_date_range_display_as_html(self, tz=None, force_show_end=False) -> str: def get_date_range_display_as_html(self, tz=None, force_show_end=False) -> str:
return self.get_date_range_display(tz, force_show_end, as_html=True) return self.get_date_range_display(tz, force_show_end, as_html=True)
@@ -742,8 +747,8 @@ class Event(EventMixin, LoggedModel):
Returns the names of the plugins activated for this event as a list. Returns the names of the plugins activated for this event as a list.
""" """
if self.plugins is None: if self.plugins is None:
return self.organizer.get_plugins() return []
return self.plugins.split(",") + self.organizer.get_plugins() return self.plugins.split(",")
def get_cache(self): def get_cache(self):
""" """
+107 -25
View File
@@ -33,17 +33,16 @@
# License for the specific language governing permissions and limitations under the License. # License for the specific language governing permissions and limitations under the License.
import json import json
import logging
from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db import models from django.db import models
from django.urls import reverse from django.urls import reverse
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _ from django.utils.html import escape
from django.utils.translation import gettext_lazy as _, pgettext_lazy
from pretix.base.logentrytypes import log_entry_types, make_link from pretix.base.signals import logentry_object_link
from pretix.base.signals import is_app_active, logentry_object_link
class VisibleOnlyManager(models.Manager): class VisibleOnlyManager(models.Manager):
@@ -93,10 +92,6 @@ class LogEntry(models.Model):
indexes = [models.Index(fields=["datetime", "id"])] indexes = [models.Index(fields=["datetime", "id"])]
def display(self): def display(self):
log_entry_type, meta = log_entry_types.find(action_type=self.action_type)
if log_entry_type:
return log_entry_type.display(self)
from ..signals import logentry_display from ..signals import logentry_display
for receiver, response in logentry_display.send(self.event, logentry=self): for receiver, response in logentry_display.send(self.event, logentry=self):
@@ -131,18 +126,10 @@ class LogEntry(models.Model):
@cached_property @cached_property
def display_object(self): def display_object(self):
from . import ( from . import (
Discount, Event, Item, Order, Question, Quota, SubEvent, Voucher, Discount, Event, Item, ItemCategory, Order, Question, Quota,
SubEvent, TaxRule, Voucher,
) )
log_entry_type, meta = log_entry_types.find(action_type=self.action_type)
if log_entry_type:
link_info = log_entry_type.get_object_link_info(self)
if is_app_active(self.event, meta['plugin']):
return make_link(link_info, log_entry_type.object_link_wrapper)
else:
return make_link(link_info, log_entry_type.object_link_wrapper, is_active=False,
event=self.event, plugin_name=meta['plugin'] and getattr(meta['plugin'], 'name'))
try: try:
if self.content_type.model_class() is Event: if self.content_type.model_class() is Event:
return '' return ''
@@ -150,15 +137,110 @@ class LogEntry(models.Model):
co = self.content_object co = self.content_object
except: except:
return '' return ''
a_map = None
a_text = None
for receiver, response in logentry_object_link.send(self.event, logentry=self): if isinstance(co, Order):
if response: a_text = _('Order {val}')
return response a_map = {
'href': reverse('control:event.order', kwargs={
'event': self.event.slug,
'organizer': self.event.organizer.slug,
'code': co.code
}),
'val': escape(co.code),
}
elif isinstance(co, Voucher):
a_text = _('Voucher {val}')
a_map = {
'href': reverse('control:event.voucher', kwargs={
'event': self.event.slug,
'organizer': self.event.organizer.slug,
'voucher': co.id
}),
'val': escape(co.code[:6]),
}
elif isinstance(co, Item):
a_text = _('Product {val}')
a_map = {
'href': reverse('control:event.item', kwargs={
'event': self.event.slug,
'organizer': self.event.organizer.slug,
'item': co.id
}),
'val': escape(co.name),
}
elif isinstance(co, SubEvent):
a_text = pgettext_lazy('subevent', 'Date {val}')
a_map = {
'href': reverse('control:event.subevent', kwargs={
'event': self.event.slug,
'organizer': self.event.organizer.slug,
'subevent': co.id
}),
'val': escape(str(co))
}
elif isinstance(co, Quota):
a_text = _('Quota {val}')
a_map = {
'href': reverse('control:event.items.quotas.show', kwargs={
'event': self.event.slug,
'organizer': self.event.organizer.slug,
'quota': co.id
}),
'val': escape(co.name),
}
elif isinstance(co, Discount):
a_text = _('Discount {val}')
a_map = {
'href': reverse('control:event.items.discounts.edit', kwargs={
'event': self.event.slug,
'organizer': self.event.organizer.slug,
'discount': co.id
}),
'val': escape(co.internal_name),
}
elif isinstance(co, ItemCategory):
a_text = _('Category {val}')
a_map = {
'href': reverse('control:event.items.categories.edit', kwargs={
'event': self.event.slug,
'organizer': self.event.organizer.slug,
'category': co.id
}),
'val': escape(co.name),
}
elif isinstance(co, Question):
a_text = _('Question {val}')
a_map = {
'href': reverse('control:event.items.questions.show', kwargs={
'event': self.event.slug,
'organizer': self.event.organizer.slug,
'question': co.id
}),
'val': escape(co.question),
}
elif isinstance(co, TaxRule):
a_text = _('Tax rule {val}')
a_map = {
'href': reverse('control:event.settings.tax.edit', kwargs={
'event': self.event.slug,
'organizer': self.event.organizer.slug,
'rule': co.id
}),
'val': escape(co.name),
}
if isinstance(co, (Order, Voucher, Item, SubEvent, Quota, Discount, Question)): if a_text and a_map:
logging.warning("LogEntryType missing or ill-defined: %s", self.action_type) a_map['val'] = '<a href="{href}">{val}</a>'.format_map(a_map)
return a_text.format_map(a_map)
return '' elif a_text:
return a_text
else:
for receiver, response in logentry_object_link.send(self.event, logentry=self):
if response:
return response
return ''
@cached_property @cached_property
def parsed_data(self): def parsed_data(self):
-13
View File
@@ -299,11 +299,6 @@ class Order(LockModel, LoggedModel):
verbose_name=_("Meta information"), verbose_name=_("Meta information"),
null=True, blank=True null=True, blank=True
) )
api_meta = models.JSONField(
verbose_name=_("API meta information"),
null=False, blank=True,
default=dict
)
last_modified = models.DateTimeField( last_modified = models.DateTimeField(
auto_now=True, db_index=False auto_now=True, db_index=False
) )
@@ -2835,14 +2830,6 @@ class OrderPosition(AbstractPosition):
(self.order.event.settings.change_allow_user_addons and ItemAddOn.objects.filter(base_item_id__in=[op.item_id for op in positions]).exists()) (self.order.event.settings.change_allow_user_addons and ItemAddOn.objects.filter(base_item_id__in=[op.item_id for op in positions]).exists())
) )
@property
def full_code(self):
"""
A ticket code which is unique among all events of a single organizer,
built by concatenating the event slug and the order code.
"""
return '{order}-{position}'.format(order=self.order.full_code, position=self.positionid)
class Transaction(models.Model): class Transaction(models.Model):
""" """
-12
View File
@@ -91,10 +91,6 @@ class Organizer(LoggedModel):
verbose_name=_("Short form"), verbose_name=_("Short form"),
unique=True unique=True
) )
plugins = models.TextField(
null=True, blank=True,
verbose_name=_("Plugins"),
)
class Meta: class Meta:
verbose_name = _("Organizer") verbose_name = _("Organizer")
@@ -123,14 +119,6 @@ class Organizer(LoggedModel):
""" """
self.settings.cookie_consent = True self.settings.cookie_consent = True
def get_plugins(self):
"""
Returns the names of the plugins activated for this organizer as a list.
"""
if self.plugins is None:
return []
return self.plugins.split(",")
def get_cache(self): def get_cache(self):
""" """
Returns an :py:class:`ObjectRelatedCache` object. This behaves equivalent to Returns an :py:class:`ObjectRelatedCache` object. This behaves equivalent to
+12 -23
View File
@@ -185,7 +185,7 @@ class Seat(models.Model):
@classmethod @classmethod
def annotated(cls, qs, event_id, subevent, ignore_voucher_id=None, minimal_distance=0, def annotated(cls, qs, event_id, subevent, ignore_voucher_id=None, minimal_distance=0,
ignore_order_id=None, ignore_cart_id=None, distance_only_within_row=False, annotate_ids=False): ignore_order_id=None, ignore_cart_id=None, distance_only_within_row=False):
from . import CartPosition, Order, OrderPosition, Voucher from . import CartPosition, Order, OrderPosition, Voucher
vqs = Voucher.objects.filter( vqs = Voucher.objects.filter(
@@ -214,24 +214,17 @@ class Seat(models.Model):
) )
if ignore_cart_id: if ignore_cart_id:
cqs = cqs.exclude(cart_id=ignore_cart_id) cqs = cqs.exclude(cart_id=ignore_cart_id)
if annotate_ids: qs_annotated = qs.annotate(
qs_annotated = qs.annotate( has_order=Exists(
orderposition_id=Subquery(opqs.values('id')), opqs
cartposition_id=Subquery(cqs.values('id')), ),
voucher_id=Subquery(vqs.values('id')), has_cart=Exists(
) cqs
else: ),
qs_annotated = qs.annotate( has_voucher=Exists(
has_order=Exists( vqs
opqs
),
has_cart=Exists(
cqs
),
has_voucher=Exists(
vqs
)
) )
)
if minimal_distance > 0: if minimal_distance > 0:
# TODO: Is there a more performant implementation on PostgreSQL using # TODO: Is there a more performant implementation on PostgreSQL using
@@ -242,11 +235,7 @@ class Seat(models.Model):
Power(F('y') - OuterRef('y'), Value(2), output_field=models.FloatField()) Power(F('y') - OuterRef('y'), Value(2), output_field=models.FloatField())
) )
).filter( ).filter(
( Q(has_order=True) | Q(has_cart=True) | Q(has_voucher=True),
(Q(orderposition_id__isnull=False) | Q(cartposition_id__isnull=False) | Q(voucher_id__isnull=False))
if annotate_ids else
(Q(has_order=True) | Q(has_cart=True) | Q(has_voucher=True))
),
distance__lt=minimal_distance ** 2 distance__lt=minimal_distance ** 2
) )
if distance_only_within_row: if distance_only_within_row:
+1 -1
View File
@@ -587,7 +587,7 @@ class BasePaymentProvider:
return rel_date.datetime(self.event).date() return rel_date.datetime(self.event).date()
def _is_available_by_time(self, now_dt=None, cart_id=None, order=None): def _is_available_by_time(self, now_dt=None, cart_id=None, order=None):
now_dt = now_dt or time_machine_now() now_dt = now_dt or now()
tz = ZoneInfo(self.event.settings.timezone) tz = ZoneInfo(self.event.settings.timezone)
try: try:
+9 -48
View File
@@ -49,7 +49,7 @@ from io import BytesIO
import jsonschema import jsonschema
import reportlab.rl_config import reportlab.rl_config
from bidi import get_display from bidi.algorithm import get_display
from django.conf import settings from django.conf import settings
from django.contrib.staticfiles import finders from django.contrib.staticfiles import finders
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
@@ -956,7 +956,7 @@ class Renderer:
) )
canvas.restoreState() canvas.restoreState()
def _text_paragraph(self, op: OrderPosition, order: Order, o: dict, legacy_lineheight=False, override_fontsize=None): def _draw_textarea(self, canvas: Canvas, op: OrderPosition, order: Order, o: dict):
font = o['fontfamily'] font = o['fontfamily']
# Since pdfmetrics.registerFont is global, we want to make sure that no one tries to sneak in a font, they # Since pdfmetrics.registerFont is global, we want to make sure that no one tries to sneak in a font, they
@@ -970,13 +970,12 @@ class Renderer:
if o['italic']: if o['italic']:
font += ' I' font += ' I'
fontsize = override_fontsize if override_fontsize is not None else float(o['fontsize'])
try: try:
ad = getAscentDescent(font, fontsize) ad = getAscentDescent(font, float(o['fontsize']))
except KeyError: # font not known, fall back except KeyError: # font not known, fall back
logger.warning(f'Use of unknown font "{font}"') logger.warning(f'Use of unknown font "{font}"')
font = 'Open Sans' font = 'Open Sans'
ad = getAscentDescent(font, fontsize) ad = getAscentDescent(font, float(o['fontsize']))
align_map = { align_map = {
'left': TA_LEFT, 'left': TA_LEFT,
@@ -986,17 +985,16 @@ class Renderer:
# lineheight display differs from browser canvas. This calc is just empirical values to get # lineheight display differs from browser canvas. This calc is just empirical values to get
# reportlab render similarly to browser canvas. # reportlab render similarly to browser canvas.
# for backwards compatability use „uncorrected“ lineheight of 1.0 instead of 1.15 # for backwards compatability use „uncorrected“ lineheight of 1.0 instead of 1.15
lineheight = float(o['lineheight']) * 1.15 if not legacy_lineheight or 'lineheight' in o else 1.0 lineheight = float(o['lineheight']) * 1.15 if 'lineheight' in o else 1.0
style = ParagraphStyle( style = ParagraphStyle(
name=uuid.uuid4().hex, name=uuid.uuid4().hex,
fontName=font, fontName=font,
fontSize=fontsize, fontSize=float(o['fontsize']),
leading=lineheight * fontsize, leading=lineheight * float(o['fontsize']),
# for backwards compatability use autoLeading if no lineheight is given # for backwards compatability use autoLeading if no lineheight is given
autoLeading='off' if not legacy_lineheight or 'lineheight' in o else 'max', autoLeading='off' if 'lineheight' in o else 'max',
textColor=Color(o['color'][0] / 255, o['color'][1] / 255, o['color'][2] / 255), textColor=Color(o['color'][0] / 255, o['color'][1] / 255, o['color'][2] / 255),
alignment=align_map[o['align']], alignment=align_map[o['align']]
splitLongWords=o.get('splitlongwords', True),
) )
# add an almost-invisible space &hairsp; after hyphens as word-wrap in ReportLab only works on space chars # add an almost-invisible space &hairsp; after hyphens as word-wrap in ReportLab only works on space chars
text = conditional_escape( text = conditional_escape(
@@ -1015,41 +1013,6 @@ class Renderer:
logger.exception('Reshaping/Bidi fixes failed on string {}'.format(repr(text))) logger.exception('Reshaping/Bidi fixes failed on string {}'.format(repr(text)))
p = Paragraph(text, style=style) p = Paragraph(text, style=style)
return p, ad, lineheight
def _draw_textcontainer(self, canvas: Canvas, op: OrderPosition, order: Order, o: dict):
fontsize = float(o['fontsize'])
height = float(o['height']) * mm
width = float(o['width']) * mm
while True:
p, ad, lineheight = self._text_paragraph(op, order, o, override_fontsize=fontsize)
w, h = p.wrapOn(canvas, width, 1000 * mm)
widths = p.getActualLineWidths0()
if not widths:
break
actual_w = max(widths)
if not o.get('autoresize', False) or (h <= height and actual_w <= width) or fontsize <= 1.0:
break
if h > height: # we can do larger steps for height
fontsize -= max(1.0, fontsize * .1)
else:
fontsize -= max(.25, fontsize * .025)
canvas.saveState()
# The ascent/descent offsets here are not really proven to be correct, they're just empirical values to get
# reportlab render similarly to browser canvas.
canvas.translate(float(o['left']) * mm, float(o['bottom']) * mm + height)
canvas.rotate(o.get('rotation', 0) * -1)
if o.get('verticalalign', 'top') == 'top':
p.drawOn(canvas, 0, - h)
elif o.get('verticalalign', 'top') == 'middle':
p.drawOn(canvas, 0, (-height - h) / 2)
elif o.get('verticalalign', 'top') == 'bottom':
p.drawOn(canvas, 0, -height)
canvas.restoreState()
def _draw_textarea(self, canvas: Canvas, op: OrderPosition, order: Order, o: dict):
p, ad, lineheight = self._text_paragraph(op, order, o, legacy_lineheight=True)
w, h = p.wrapOn(canvas, float(o['width']) * mm, 1000 * mm) w, h = p.wrapOn(canvas, float(o['width']) * mm, 1000 * mm)
# p_size = p.wrap(float(o['width']) * mm, 1000 * mm) # p_size = p.wrap(float(o['width']) * mm, 1000 * mm)
canvas.saveState() canvas.saveState()
@@ -1088,8 +1051,6 @@ class Renderer:
self._draw_barcodearea(canvas, op, order, o) self._draw_barcodearea(canvas, op, order, o)
elif o['type'] == "imagearea": elif o['type'] == "imagearea":
self._draw_imagearea(canvas, op, order, o) self._draw_imagearea(canvas, op, order, o)
elif o['type'] == "textcontainer":
self._draw_textcontainer(canvas, op, order, o)
elif o['type'] == "textarea": elif o['type'] == "textarea":
self._draw_textarea(canvas, op, order, o) self._draw_textarea(canvas, op, order, o)
elif o['type'] == "poweredby": elif o['type'] == "poweredby":
+2 -8
View File
@@ -275,7 +275,7 @@ class CartManager:
} }
def __init__(self, event: Event, cart_id: str, sales_channel: SalesChannel, def __init__(self, event: Event, cart_id: str, sales_channel: SalesChannel,
invoice_address: InvoiceAddress=None, widget_data=None, expiry=None): invoice_address: InvoiceAddress=None, widget_data=None):
self.event = event self.event = event
self.cart_id = cart_id self.cart_id = cart_id
self.real_now_dt = now() self.real_now_dt = now()
@@ -287,7 +287,6 @@ class CartManager:
self._variations_cache = {} self._variations_cache = {}
self._seated_cache = {} self._seated_cache = {}
self._expiry = None self._expiry = None
self._explicit_expiry = expiry
self.invoice_address = invoice_address self.invoice_address = invoice_address
self._widget_data = widget_data or {} self._widget_data = widget_data or {}
self._sales_channel = sales_channel self._sales_channel = sales_channel
@@ -306,12 +305,7 @@ class CartManager:
return self._seated_cache[item, subevent] return self._seated_cache[item, subevent]
def _calculate_expiry(self): def _calculate_expiry(self):
if self._explicit_expiry: self._expiry = self.real_now_dt + timedelta(minutes=self.event.settings.get('reservation_time', as_type=int))
self._expiry = self._explicit_expiry
else:
self._expiry = self.real_now_dt + timedelta(
minutes=self.event.settings.get('reservation_time', as_type=int)
)
def _check_presale_dates(self): def _check_presale_dates(self):
if self.event.presale_start and time_machine_now(self.real_now_dt) < self.event.presale_start: if self.event.presale_start and time_machine_now(self.real_now_dt) < self.event.presale_start:
+2 -2
View File
@@ -1154,7 +1154,7 @@ def perform_checkin(op: OrderPosition, clist: CheckinList, given_answers: dict,
) )
@receiver(order_placed, dispatch_uid="legacy_autocheckin_order_placed") @receiver(order_placed, dispatch_uid="autocheckin_order_placed")
def order_placed(sender, **kwargs): def order_placed(sender, **kwargs):
order = kwargs['order'] order = kwargs['order']
event = sender event = sender
@@ -1171,7 +1171,7 @@ def order_placed(sender, **kwargs):
checkin_created.send(event, checkin=ci) checkin_created.send(event, checkin=ci)
@receiver(periodic_task, dispatch_uid="autocheckout_exit_all") @receiver(periodic_task, dispatch_uid="autocheckin_exit_all")
@scopes_disabled() @scopes_disabled()
def process_exit_all(sender, **kwargs): def process_exit_all(sender, **kwargs):
qs = CheckinList.objects.filter( qs = CheckinList.objects.filter(
+5 -18
View File
@@ -58,7 +58,6 @@ from django.core.mail import (
from django.core.mail.message import SafeMIMEText from django.core.mail.message import SafeMIMEText
from django.db import transaction from django.db import transaction
from django.template.loader import get_template from django.template.loader import get_template
from django.utils.html import escape
from django.utils.timezone import now, override from django.utils.timezone import now, override
from django.utils.translation import gettext as _, pgettext from django.utils.translation import gettext as _, pgettext
from django_scopes import scope, scopes_disabled from django_scopes import scope, scopes_disabled
@@ -110,22 +109,6 @@ def clean_sender_name(sender_name: str) -> str:
return sender_name return sender_name
def prefix_subject(settings_holder, subject, highlight=False):
prefix = settings_holder.settings.get('mail_prefix')
if prefix and prefix.startswith('[') and prefix.endswith(']'):
prefix = prefix[1:-1]
if prefix:
prefix = f"[{prefix}]"
if highlight:
prefix = '<span class="placeholder" title="{}">{}</span>'.format(
_('This prefix has been set in your event or organizer settings.'),
escape(prefix)
)
subject = f"{prefix} {subject}"
return subject
def mail(email: Union[str, Sequence[str]], subject: str, template: Union[str, LazyI18nString], def mail(email: Union[str, Sequence[str]], subject: str, template: Union[str, LazyI18nString],
context: Dict[str, Any] = None, event: Event = None, locale: str = None, order: Order = None, context: Dict[str, Any] = None, event: Event = None, locale: str = None, order: Order = None,
position: OrderPosition = None, *, headers: dict = None, sender: str = None, organizer: Organizer = None, position: OrderPosition = None, *, headers: dict = None, sender: str = None, organizer: Organizer = None,
@@ -257,7 +240,11 @@ def mail(email: Union[str, Sequence[str]], subject: str, template: Union[str, La
and settings_holder.settings.contact_mail and not headers.get('Reply-To'): and settings_holder.settings.contact_mail and not headers.get('Reply-To'):
headers['Reply-To'] = settings_holder.settings.contact_mail headers['Reply-To'] = settings_holder.settings.contact_mail
subject = prefix_subject(settings_holder, subject) prefix = settings_holder.settings.get('mail_prefix')
if prefix and prefix.startswith('[') and prefix.endswith(']'):
prefix = prefix[1:-1]
if prefix:
subject = "[%s] %s" % (prefix, subject)
body_plain += "\r\n\r\n-- \r\n" body_plain += "\r\n\r\n-- \r\n"
+6 -9
View File
@@ -960,7 +960,7 @@ def _get_fees(positions: List[CartPosition], payment_requests: List[dict], addre
def _create_order(event: Event, *, email: str, positions: List[CartPosition], now_dt: datetime, def _create_order(event: Event, *, email: str, positions: List[CartPosition], now_dt: datetime,
payment_requests: List[dict], sales_channel: SalesChannel, locale: str=None, payment_requests: List[dict], sales_channel: SalesChannel, locale: str=None,
address: InvoiceAddress=None, meta_info: dict=None, shown_total=None, address: InvoiceAddress=None, meta_info: dict=None, shown_total=None,
customer=None, valid_if_pending=False, api_meta: dict=None): customer=None, valid_if_pending=False):
payments = [] payments = []
try: try:
@@ -985,7 +985,6 @@ def _create_order(event: Event, *, email: str, positions: List[CartPosition], no
total=total, total=total,
testmode=True if sales_channel.type_instance.testmode_supported and event.testmode else False, testmode=True if sales_channel.type_instance.testmode_supported and event.testmode else False,
meta_info=json.dumps(meta_info or {}), meta_info=json.dumps(meta_info or {}),
api_meta=api_meta or {},
require_approval=require_approval, require_approval=require_approval,
sales_channel=sales_channel, sales_channel=sales_channel,
customer=customer, customer=customer,
@@ -1097,7 +1096,7 @@ def _order_placed_email_attendee(event: Event, order: Order, position: OrderPosi
def _perform_order(event: Event, payment_requests: List[dict], position_ids: List[str], def _perform_order(event: Event, payment_requests: List[dict], position_ids: List[str],
email: str, locale: str, address: int, meta_info: dict=None, sales_channel: str='web', email: str, locale: str, address: int, meta_info: dict=None, sales_channel: str='web',
shown_total=None, customer=None, api_meta: dict=None): shown_total=None, customer=None):
for p in payment_requests: for p in payment_requests:
p['pprov'] = event.get_payment_providers(cached=True)[p['provider']] p['pprov'] = event.get_payment_providers(cached=True)[p['provider']]
if not p['pprov']: if not p['pprov']:
@@ -1201,8 +1200,7 @@ def _perform_order(event: Event, payment_requests: List[dict], position_ids: Lis
sales_channel=sales_channel, sales_channel=sales_channel,
shown_total=shown_total, shown_total=shown_total,
customer=customer, customer=customer,
valid_if_pending=valid_if_pending, valid_if_pending=valid_if_pending
api_meta=api_meta,
) )
try: try:
@@ -2875,13 +2873,12 @@ class OrderChangeManager:
@app.task(base=ProfiledEventTask, bind=True, max_retries=5, default_retry_delay=1, throws=(OrderError,)) @app.task(base=ProfiledEventTask, bind=True, max_retries=5, default_retry_delay=1, throws=(OrderError,))
def perform_order(self, event: Event, payments: List[dict], positions: List[str], def perform_order(self, event: Event, payments: List[dict], positions: List[str],
email: str=None, locale: str=None, address: int=None, meta_info: dict=None, email: str=None, locale: str=None, address: int=None, meta_info: dict=None,
sales_channel: str='web', shown_total=None, customer=None, override_now_dt: datetime=None, sales_channel: str='web', shown_total=None, customer=None, override_now_dt: datetime=None):
api_meta: dict=None):
with language(locale), time_machine_now_assigned(override_now_dt): with language(locale), time_machine_now_assigned(override_now_dt):
try: try:
try: try:
return _perform_order(event, payments, positions, email, locale, address, meta_info, return _perform_order(event, payments, positions, email, locale, address, meta_info,
sales_channel, shown_total, customer, api_meta) sales_channel, shown_total, customer)
except LockTimeoutException: except LockTimeoutException:
self.retry() self.retry()
except (MaxRetriesExceededError, LockTimeoutException): except (MaxRetriesExceededError, LockTimeoutException):
@@ -3152,7 +3149,7 @@ def signal_listener_issue_memberships(sender: Event, order: Order, **kwargs):
if order.status != Order.STATUS_PAID or not order.customer: if order.status != Order.STATUS_PAID or not order.customer:
return return
for p in order.positions.all(): for p in order.positions.all():
if p.item.grant_membership_type_id and not p.granted_memberships.exists(): if p.item.grant_membership_type_id:
create_membership(order.customer, p) create_membership(order.customer, p)
+2 -19
View File
@@ -1295,8 +1295,7 @@ DEFAULTS = {
'form_kwargs': dict( 'form_kwargs': dict(
label=_("Show event times and dates on the ticket shop"), label=_("Show event times and dates on the ticket shop"),
help_text=_("If disabled, no date or time will be shown on the ticket shop's front page. This settings " help_text=_("If disabled, no date or time will be shown on the ticket shop's front page. This settings "
"also affects a few other locations, however it should not be expected that the date of the " "does however not affect the display in other locations."),
"event is shown nowhere to users."),
) )
}, },
'show_date_to': { 'show_date_to': {
@@ -1479,10 +1478,6 @@ DEFAULTS = {
min_value=1, min_value=1,
required=True, required=True,
widget=forms.NumberInput(), widget=forms.NumberInput(),
help_text=_('With an increased limit, a customer may request more than one ticket for a specific product '
'using the same, unique email address. However, regardless of this setting, they will need to '
'fill the waiting list form multiple times if they want more than one ticket, as every entry only '
'grants one single ticket at a time.'),
) )
}, },
'show_checkin_number_user': { 'show_checkin_number_user': {
@@ -3364,9 +3359,7 @@ Your {organizer} team""")) # noqa: W291
}, },
'seating_allow_blocked_seats_for_channel': { 'seating_allow_blocked_seats_for_channel': {
'default': [], 'default': [],
'type': list, 'type': list
'serializer_class': serializers.ListField,
'serializer_kwargs': lambda: dict(child=serializers.CharField()),
}, },
'seating_distance_within_row': { 'seating_distance_within_row': {
'default': 'False', 'default': 'False',
@@ -3804,16 +3797,6 @@ def validate_event_settings(event, settings_dict):
'payment_term_last': _('The last payment date cannot be before the end of presale.') 'payment_term_last': _('The last payment date cannot be before the end of presale.')
}) })
if settings_dict.get('seating_allow_blocked_seats_for_channel'):
allowed_channels = set(event.organizer.sales_channels.values_list("identifier", flat=True))
for channel in settings_dict['seating_allow_blocked_seats_for_channel']:
if channel not in allowed_channels:
raise ValidationError({
'seating_allow_blocked_seats_for_channel': _('The value "{identifier}" is not a valid sales channel.').format(
identifier=channel
)
})
if isinstance(event, Event): if isinstance(event, Event):
validate_event_settings.send(sender=event, settings_dict=settings_dict) validate_event_settings.send(sender=event, settings_dict=settings_dict)
+43 -106
View File
@@ -52,71 +52,49 @@ def _populate_app_cache():
app_cache[ac.name] = ac app_cache[ac.name] = ac
def get_defining_app(o): class EventPluginSignal(django.dispatch.Signal):
# If sentry packed this in a wrapper, unpack that
if "sentry" in o.__module__:
o = o.__wrapped__
# Find the Django application this belongs to
searchpath = o.__module__
# Core modules are always active
if any(searchpath.startswith(cm) for cm in settings.CORE_MODULES):
return 'CORE'
if not app_cache:
_populate_app_cache()
while True:
app = app_cache.get(searchpath)
if "." not in searchpath or app:
break
searchpath, _ = searchpath.rsplit(".", 1)
return app
def is_app_active(sender, app):
if app == 'CORE':
return True
excluded = settings.PRETIX_PLUGINS_EXCLUDE
if sender and app and app.name in sender.get_plugins() and app.name not in excluded:
if not hasattr(app, 'compatibility_errors') or not app.compatibility_errors:
return True
return False
def is_receiver_active(sender, receiver):
if sender is None:
# Send to all events!
return True
app = get_defining_app(receiver)
return is_app_active(sender, app)
def is_plugin_host(sender):
return hasattr(sender, 'get_plugins')
class PluginSignal(django.dispatch.Signal):
""" """
This is an extension to Django's built-in signals which differs in a way that it sends This is an extension to Django's built-in signals which differs in a way that it sends
out it's events only to receivers which belong to plugins that are enabled for the given out it's events only to receivers which belong to plugins that are enabled for the given
Event. Event.
""" """
def send(self, sender, **named) -> List[Tuple[Callable, Any]]: def _is_active(self, sender, receiver):
if sender is None:
# Send to all events!
return True
# If sentry packed this in a wrapper, unpack that
if "sentry" in receiver.__module__:
receiver = receiver.__wrapped__
# Find the Django application this belongs to
searchpath = receiver.__module__
core_module = any([searchpath.startswith(cm) for cm in settings.CORE_MODULES])
app = None
if not core_module:
while True:
app = app_cache.get(searchpath)
if "." not in searchpath or app:
break
searchpath, _ = searchpath.rsplit(".", 1)
# Only fire receivers from active plugins and core modules
excluded = settings.PRETIX_PLUGINS_EXCLUDE
if core_module or (sender and app and app.name in sender.get_plugins() and app.name not in excluded):
if not hasattr(app, 'compatibility_errors') or not app.compatibility_errors:
return True
return False
def send(self, sender: Event, **named) -> List[Tuple[Callable, Any]]:
""" """
Send signal from sender to all connected receivers that belong to Send signal from sender to all connected receivers that belong to
plugins enabled for the given Event. plugins enabled for the given Event.
sender is required to be a plugin host (an object with a `get_plugins` method), sender is required to be an instance of ``pretix.base.models.Event``.
for example a ``pretix.base.models.Event``.
""" """
if sender and not is_plugin_host(sender): if sender and not isinstance(sender, Event):
raise ValueError("Sender needs to be a plugin host.") raise ValueError("Sender needs to be an event.")
responses = [] responses = []
if not self.receivers or self.sender_receivers_cache.get(sender) is NO_RECEIVERS: if not self.receivers or self.sender_receivers_cache.get(sender) is NO_RECEIVERS:
@@ -126,22 +104,21 @@ class PluginSignal(django.dispatch.Signal):
_populate_app_cache() _populate_app_cache()
for receiver in self._sorted_receivers(sender): for receiver in self._sorted_receivers(sender):
if is_receiver_active(sender, receiver): if self._is_active(sender, receiver):
response = receiver(signal=self, sender=sender, **named) response = receiver(signal=self, sender=sender, **named)
responses.append((receiver, response)) responses.append((receiver, response))
return responses return responses
def send_chained(self, sender, chain_kwarg_name, **named) -> List[Tuple[Callable, Any]]: def send_chained(self, sender: Event, chain_kwarg_name, **named) -> List[Tuple[Callable, Any]]:
""" """
Send signal from sender to all connected receivers. The return value of the first receiver Send signal from sender to all connected receivers. The return value of the first receiver
will be used as the keyword argument specified by ``chain_kwarg_name`` in the input to the will be used as the keyword argument specified by ``chain_kwarg_name`` in the input to the
second receiver and so on. The return value of the last receiver is returned by this method. second receiver and so on. The return value of the last receiver is returned by this method.
sender is required to be a plugin host (an object with a `get_plugins` method), sender is required to be an instance of ``pretix.base.models.Event``.
for example a ``pretix.base.models.Event``.
""" """
if sender and not is_plugin_host(sender): if sender and not isinstance(sender, Event):
raise ValueError("Sender needs to be a plugin host.") raise ValueError("Sender needs to be an event.")
response = named.get(chain_kwarg_name) response = named.get(chain_kwarg_name)
if not self.receivers or self.sender_receivers_cache.get(sender) is NO_RECEIVERS: if not self.receivers or self.sender_receivers_cache.get(sender) is NO_RECEIVERS:
@@ -151,22 +128,21 @@ class PluginSignal(django.dispatch.Signal):
_populate_app_cache() _populate_app_cache()
for receiver in self._sorted_receivers(sender): for receiver in self._sorted_receivers(sender):
if is_receiver_active(sender, receiver): if self._is_active(sender, receiver):
named[chain_kwarg_name] = response named[chain_kwarg_name] = response
response = receiver(signal=self, sender=sender, **named) response = receiver(signal=self, sender=sender, **named)
return response return response
def send_robust(self, sender, **named) -> List[Tuple[Callable, Any]]: def send_robust(self, sender: Event, **named) -> List[Tuple[Callable, Any]]:
""" """
Send signal from sender to all connected receivers. If a receiver raises an exception Send signal from sender to all connected receivers. If a receiver raises an exception
instead of returning a value, the exception is included as the result instead of instead of returning a value, the exception is included as the result instead of
stopping the response chain at the offending receiver. stopping the response chain at the offending receiver.
sender is required to be a plugin host (an object with a `get_plugins` method), sender is required to be an instance of ``pretix.base.models.Event``.
for example a ``pretix.base.models.Event``.
""" """
if sender and not is_plugin_host(sender): if sender and not isinstance(sender, Event):
raise ValueError("Sender needs to be a plugin host.") raise ValueError("Sender needs to be an event.")
responses = [] responses = []
if ( if (
@@ -179,7 +155,7 @@ class PluginSignal(django.dispatch.Signal):
_populate_app_cache() _populate_app_cache()
for receiver in self._sorted_receivers(sender): for receiver in self._sorted_receivers(sender):
if is_receiver_active(sender, receiver): if self._is_active(sender, receiver):
try: try:
response = receiver(signal=self, sender=sender, **named) response = receiver(signal=self, sender=sender, **named)
except Exception as err: except Exception as err:
@@ -201,14 +177,6 @@ class PluginSignal(django.dispatch.Signal):
return sorted_list return sorted_list
class OrganizerPluginSignal(PluginSignal):
pass
class EventPluginSignal(PluginSignal):
pass
class GlobalSignal(django.dispatch.Signal): class GlobalSignal(django.dispatch.Signal):
def send_chained(self, sender: Event, chain_kwarg_name, **named) -> List[Tuple[Callable, Any]]: def send_chained(self, sender: Event, chain_kwarg_name, **named) -> List[Tuple[Callable, Any]]:
""" """
@@ -234,37 +202,6 @@ class DeprecatedSignal(django.dispatch.Signal):
super().connect(receiver, sender=None, weak=True, dispatch_uid=None) super().connect(receiver, sender=None, weak=True, dispatch_uid=None)
class Registry:
def __init__(self, keys):
self.registered_items = list()
self.keys = keys
self.by_key = {key: {} for key in self.keys.keys()}
def register(self, *objs):
for obj in objs:
meta = {k: accessor(obj) for k, accessor in self.keys.items()}
tup = (obj, meta)
for key, accessor in self.keys.items():
self.by_key[key][accessor(obj)] = tup
self.registered_items.append(tup)
def new(self, *args, **kwargs):
def reg(clz):
obj = clz(*args, **kwargs)
self.register(obj)
return clz
return reg
def find(self, **kwargs):
(key, value), = kwargs.items()
return self.by_key.get(key).get(value, (None, None))
class PluginRegistry(Registry):
def __init__(self, keys):
super().__init__({"plugin": lambda o: get_defining_app(o), **keys})
event_live_issues = EventPluginSignal() event_live_issues = EventPluginSignal()
""" """
This signal is sent out to determine whether an event can be taken live. If you want to This signal is sent out to determine whether an event can be taken live. If you want to
+11 -89
View File
@@ -48,7 +48,6 @@ from django.utils.formats import date_format, localize
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.timezone import get_current_timezone, make_aware, now from django.utils.timezone import get_current_timezone, make_aware, now
from django.utils.translation import gettext, gettext_lazy as _, pgettext_lazy from django.utils.translation import gettext, gettext_lazy as _, pgettext_lazy
from django_countries.fields import CountryField
from django_scopes.forms import SafeModelChoiceField from django_scopes.forms import SafeModelChoiceField
from pretix.base.forms.widgets import ( from pretix.base.forms.widgets import (
@@ -61,7 +60,6 @@ from pretix.base.models import (
SubEvent, SubEventMetaValue, Team, TeamAPIToken, TeamInvite, Voucher, SubEvent, SubEventMetaValue, Team, TeamAPIToken, TeamInvite, Voucher,
) )
from pretix.base.signals import register_payment_providers from pretix.base.signals import register_payment_providers
from pretix.control.forms import SplitDateTimeField
from pretix.control.forms.widgets import Select2, Select2ItemVarQuota from pretix.control.forms.widgets import Select2, Select2ItemVarQuota
from pretix.control.signals import order_search_filter_q from pretix.control.signals import order_search_filter_q
from pretix.helpers.countries import CachedCountries from pretix.helpers.countries import CachedCountries
@@ -69,7 +67,7 @@ from pretix.helpers.database import (
get_deterministic_ordering, rolledback_transaction, get_deterministic_ordering, rolledback_transaction,
) )
from pretix.helpers.dicts import move_to_end from pretix.helpers.dicts import move_to_end
from pretix.helpers.i18n import get_format_without_seconds, i18ncomp from pretix.helpers.i18n import i18ncomp
PAYMENT_PROVIDERS = [] PAYMENT_PROVIDERS = []
@@ -689,71 +687,11 @@ class EventOrderExpertFilterForm(EventOrderFilterForm):
) )
self.fields['quota'].widget.choices = self.fields['quota'].choices self.fields['quota'].widget.choices = self.fields['quota'].choices
for q in self.event.questions.all(): for q in self.event.questions.all():
kwargs = { self.fields['question_{}'.format(q.pk)] = forms.CharField(
"label": q.question, label=q.question,
"required": False, required=False,
} help_text=_('Exact matches only')
fname = 'question_{}'.format(q.pk) )
if q.type == Question.TYPE_NUMBER:
self.fields[fname] = forms.DecimalField(
help_text=_('Exact matches only'),
**kwargs,
)
elif q.type == Question.TYPE_BOOLEAN:
self.fields[fname] = forms.ChoiceField(
choices=(
("", ""),
("True", _("Yes")),
("False", _("No")),
),
**kwargs,
)
elif q.type in (Question.TYPE_CHOICE, Question.TYPE_CHOICE_MULTIPLE):
self.fields[fname] = forms.ModelChoiceField(
queryset=q.options,
widget=forms.Select,
to_field_name='identifier',
empty_label='',
**kwargs,
)
elif q.type == Question.TYPE_COUNTRYCODE:
self.fields[fname] = CountryField(
countries=CachedCountries,
blank=True, null=True, blank_label=' ',
).formfield(
**kwargs,
widget=forms.Select,
empty_label=' ',
)
elif q.type == Question.TYPE_DATE:
self.fields[fname] = forms.DateField(
widget=DatePickerWidget(),
help_text=_('Exact matches only'),
**kwargs,
)
elif q.type == Question.TYPE_TIME:
self.fields[fname] = forms.TimeField(
widget=TimePickerWidget(time_format=get_format_without_seconds('TIME_INPUT_FORMATS')),
help_text=_('Exact matches only'),
**kwargs,
)
elif q.type == Question.TYPE_DATETIME:
self.fields[fname] = SplitDateTimeField(
widget=SplitDateTimePickerWidget(
time_format=get_format_without_seconds('TIME_INPUT_FORMATS'),
min_date=q.valid_datetime_min,
max_date=q.valid_datetime_max
),
help_text=_('Exact matches only'),
**kwargs,
)
elif q.type == Question.TYPE_FILE:
continue
else:
self.fields[fname] = forms.CharField(
help_text=_('Exact matches only'),
**kwargs,
)
def filter_qs(self, qs): def filter_qs(self, qs):
fdata = self.cleaned_data fdata = self.cleaned_data
@@ -849,24 +787,11 @@ class EventOrderExpertFilterForm(EventOrderFilterForm):
).distinct() ).distinct()
for q in self.event.questions.all(): for q in self.event.questions.all():
if fdata.get(f'question_{q.pk}'): if fdata.get(f'question_{q.pk}'):
if q.type == Question.TYPE_BOOLEAN: answers = QuestionAnswer.objects.filter(
answers = QuestionAnswer.objects.filter( question_id=q.pk,
question_id=q.pk, orderposition__order_id=OuterRef('pk'),
orderposition__order_id=OuterRef('pk'), answer__iexact=fdata.get(f'question_{q.pk}')
answer__exact=fdata.get(f'question_{q.pk}') )
)
elif q.type in (Question.TYPE_CHOICE, Question.TYPE_CHOICE_MULTIPLE):
answers = QuestionAnswer.objects.filter(
question_id=q.pk,
orderposition__order_id=OuterRef('pk'),
options=fdata.get(f'question_{q.pk}')
)
else:
answers = QuestionAnswer.objects.filter(
question_id=q.pk,
orderposition__order_id=OuterRef('pk'),
answer__iexact=fdata.get(f'question_{q.pk}')
)
qs = qs.annotate(**{f'q_{q.pk}': Exists(answers)}).filter(**{f'q_{q.pk}': True}) qs = qs.annotate(**{f'q_{q.pk}': Exists(answers)}).filter(**{f'q_{q.pk}': True})
return qs return qs
@@ -2652,9 +2577,6 @@ class DeviceFilterForm(FilterForm):
if fdata.get('gate'): if fdata.get('gate'):
qs = qs.filter(gate=fdata['gate']) qs = qs.filter(gate=fdata['gate'])
if fdata.get('software_brand'):
qs = qs.filter(software_brand=fdata['software_brand'])
if fdata.get('state') == 'active': if fdata.get('state') == 'active':
qs = qs.filter(revoked=False) qs = qs.filter(revoked=False)
elif fdata.get('state') == 'revoked': elif fdata.get('state') == 'revoked':
+12 -10
View File
@@ -40,6 +40,7 @@ from urllib.parse import urlencode
from django import forms from django import forms
from django.conf import settings from django.conf import settings
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.files.uploadedfile import UploadedFile
from django.db.models import Max from django.db.models import Max
from django.forms.formsets import DELETION_FIELD_NAME from django.forms.formsets import DELETION_FIELD_NAME
from django.urls import reverse from django.urls import reverse
@@ -62,9 +63,9 @@ from pretix.base.models import (
from pretix.base.models.items import ItemAddOn, ItemBundle, ItemMetaValue from pretix.base.models.items import ItemAddOn, ItemBundle, ItemMetaValue
from pretix.base.signals import item_copy_data from pretix.base.signals import item_copy_data
from pretix.control.forms import ( from pretix.control.forms import (
ButtonGroupRadioSelect, ExtFileField, ItemMultipleChoiceField, ButtonGroupRadioSelect, ItemMultipleChoiceField,
SalesChannelCheckboxSelectMultiple, SplitDateTimeField, SalesChannelCheckboxSelectMultiple, SizeValidationMixin,
SplitDateTimePickerWidget, SplitDateTimeField, SplitDateTimePickerWidget,
) )
from pretix.control.forms.widgets import Select2, Select2ItemVarMulti from pretix.control.forms.widgets import Select2, Select2ItemVarMulti
from pretix.helpers.models import modelcopy from pretix.helpers.models import modelcopy
@@ -561,13 +562,6 @@ class TicketNullBooleanSelect(forms.NullBooleanSelect):
class ItemUpdateForm(I18nModelForm): class ItemUpdateForm(I18nModelForm):
picture = ExtFileField(
label=_('Product picture'),
ext_whitelist=settings.FILE_UPLOAD_EXTENSIONS_IMAGE,
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
required=False,
)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.fields['tax_rule'].queryset = self.instance.event.tax_rules.all() self.fields['tax_rule'].queryset = self.instance.event.tax_rules.all()
@@ -757,6 +751,14 @@ class ItemUpdateForm(I18nModelForm):
return d return d
def clean_picture(self):
value = self.cleaned_data.get('picture')
if isinstance(value, UploadedFile) and value.size > settings.FILE_UPLOAD_MAX_SIZE_IMAGE:
raise forms.ValidationError(_("Please do not upload files larger than {size}!").format(
size=SizeValidationMixin._sizeof_fmt(settings.FILE_UPLOAD_MAX_SIZE_IMAGE)
))
return value
class Meta: class Meta:
model = Item model = Item
localized_fields = '__all__' localized_fields = '__all__'
+2 -9
View File
@@ -58,8 +58,7 @@ from pretix.api.models import WebHook
from pretix.api.webhooks import get_all_webhook_events from pretix.api.webhooks import get_all_webhook_events
from pretix.base.customersso.oidc import oidc_validate_and_complete_config from pretix.base.customersso.oidc import oidc_validate_and_complete_config
from pretix.base.forms import ( from pretix.base.forms import (
SECRET_REDACTED, I18nMarkdownTextarea, I18nModelForm, PlaceholderValidator, I18nMarkdownTextarea, I18nModelForm, PlaceholderValidator, SettingsForm,
SecretKeySettingsField, SettingsForm,
) )
from pretix.base.forms.questions import ( from pretix.base.forms.questions import (
NamePartsFormField, WrappedPhoneNumberPrefixWidget, get_country_by_locale, NamePartsFormField, WrappedPhoneNumberPrefixWidget, get_country_by_locale,
@@ -959,7 +958,7 @@ class SSOProviderForm(I18nModelForm):
label=pgettext_lazy('sso_oidc', 'Client ID'), label=pgettext_lazy('sso_oidc', 'Client ID'),
required=False, required=False,
) )
config_oidc_client_secret = SecretKeySettingsField( config_oidc_client_secret = forms.CharField(
label=pgettext_lazy('sso_oidc', 'Client secret'), label=pgettext_lazy('sso_oidc', 'Client secret'),
required=False, required=False,
) )
@@ -1016,13 +1015,7 @@ class SSOProviderForm(I18nModelForm):
if self.instance and self.instance.method == method: if self.instance and self.instance.method == method:
f.initial = self.instance.configuration.get(suffix) f.initial = self.instance.configuration.get(suffix)
def _unmask_secret_fields(self):
for k, v in self.cleaned_data.items():
if isinstance(self.fields.get(k), SecretKeySettingsField) and self.cleaned_data.get(k) == SECRET_REDACTED:
self.cleaned_data[k] = self.fields[k].initial
def clean(self): def clean(self):
self._unmask_secret_fields()
data = self.cleaned_data data = self.cleaned_data
if not data.get("method"): if not data.get("method"):
return data return data
-8
View File
@@ -98,14 +98,6 @@ class ControlFieldRenderer(FieldRenderer):
attrs = '' attrs = ''
return '<div class="{klass}"{attrs}>{html}</div>'.format(klass=self.get_form_group_class(), html=html, attrs=attrs) return '<div class="{klass}"{attrs}>{html}</div>'.format(klass=self.get_form_group_class(), html=html, attrs=attrs)
def wrap_widget(self, html):
if isinstance(self.widget, CheckboxInput):
css_class = "checkbox"
if self.field.field.disabled:
css_class += " disabled"
html = f'<div class="{css_class}">{html}</div>'
return html
class ControlFieldWithVisibilityRenderer(ControlFieldRenderer): class ControlFieldWithVisibilityRenderer(ControlFieldRenderer):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
+2 -9
View File
@@ -49,7 +49,6 @@ from pretix.base.forms import (
I18nModelForm, MarkdownTextarea, PlaceholderValidator, I18nModelForm, MarkdownTextarea, PlaceholderValidator,
) )
from pretix.base.forms.widgets import format_placeholders_help_text from pretix.base.forms.widgets import format_placeholders_help_text
from pretix.base.i18n import language
from pretix.base.models import Item, Voucher from pretix.base.models import Item, Voucher
from pretix.control.forms import SplitDateTimeField, SplitDateTimePickerWidget from pretix.control.forms import SplitDateTimeField, SplitDateTimePickerWidget
from pretix.control.forms.widgets import Select2, Select2ItemVarQuota from pretix.control.forms.widgets import Select2, Select2ItemVarQuota
@@ -290,9 +289,8 @@ class VoucherBulkForm(VoucherForm):
) )
}), }),
required=False, required=False,
help_text=_('You can either supply a list of email addresses with one email address per line, or the contents ' help_text=_('You can either supply a list of email addresses with one email address per line, or a CSV file with a title column '
'of a CSV file with a title column and one or more of the columns "email", "number", "name", ' 'and one or more of the columns "email", "number", "name", or "tag".')
'or "tag".')
) )
Recipient = namedtuple('Recipient', 'email number name tag') Recipient = namedtuple('Recipient', 'email number name tag')
@@ -334,11 +332,6 @@ class VoucherBulkForm(VoucherForm):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self._set_field_placeholders('send_subject', ['event', 'name']) self._set_field_placeholders('send_subject', ['event', 'name'])
self._set_field_placeholders('send_message', ['event', 'voucher_list', 'name']) self._set_field_placeholders('send_message', ['event', 'voucher_list', 'name'])
with language(self.instance.event.settings.locale, self.instance.event.settings.region):
for f in ("send_subject", "send_message"):
self.fields[f].initial = str(self.fields[f].initial)
if 'seat' in self.fields: if 'seat' in self.fields:
self.fields['seats'] = forms.CharField( self.fields['seats'] = forms.CharField(
label=_("Specific seat IDs"), label=_("Specific seat IDs"),
+345 -468
View File
@@ -47,19 +47,11 @@ from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _, pgettext_lazy from django.utils.translation import gettext_lazy as _, pgettext_lazy
from i18nfield.strings import LazyI18nString from i18nfield.strings import LazyI18nString
from pretix.base.logentrytypes import (
DiscountLogEntryType, EventLogEntryType, ItemCategoryLogEntryType,
ItemLogEntryType, LogEntryType, OrderLogEntryType, QuestionLogEntryType,
QuotaLogEntryType, TaxRuleLogEntryType, VoucherLogEntryType,
log_entry_types,
)
from pretix.base.models import ( from pretix.base.models import (
Checkin, CheckinList, Event, ItemVariation, LogEntry, OrderPosition, Checkin, CheckinList, Event, ItemVariation, LogEntry, OrderPosition,
TaxRule, TaxRule,
) )
from pretix.base.signals import ( from pretix.base.signals import logentry_display, orderposition_blocked_display
app_cache, logentry_display, orderposition_blocked_display,
)
from pretix.base.templatetags.money import money_filter from pretix.base.templatetags.money import money_filter
OVERVIEW_BANLIST = [ OVERVIEW_BANLIST = [
@@ -336,6 +328,278 @@ def _display_checkin(event, logentry):
@receiver(signal=logentry_display, dispatch_uid="pretixcontrol_logentry_display") @receiver(signal=logentry_display, dispatch_uid="pretixcontrol_logentry_display")
def pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs): def pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs):
plains = {
'pretix.object.cloned': _('This object has been created by cloning.'),
'pretix.organizer.changed': _('The organizer has been changed.'),
'pretix.organizer.settings': _('The organizer settings have been changed.'),
'pretix.organizer.footerlinks.changed': _('The footer links have been changed.'),
'pretix.organizer.export.schedule.added': _('A scheduled export has been added.'),
'pretix.organizer.export.schedule.changed': _('A scheduled export has been changed.'),
'pretix.organizer.export.schedule.deleted': _('A scheduled export has been deleted.'),
'pretix.organizer.export.schedule.executed': _('A scheduled export has been executed.'),
'pretix.organizer.export.schedule.failed': _('A scheduled export has failed: {reason}.'),
'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.'),
'pretix.giftcards.acceptance.acceptor.removed': _('A gift card acceptor has been removed.'),
'pretix.giftcards.acceptance.issuer.removed': _('A gift card issuer has been removed or declined.'),
'pretix.giftcards.acceptance.issuer.accepted': _('A new gift card issuer has been accepted.'),
'pretix.webhook.created': _('The webhook has been created.'),
'pretix.webhook.changed': _('The webhook has been changed.'),
'pretix.webhook.retries.expedited': _('The webhook call retry jobs have been manually expedited.'),
'pretix.webhook.retries.dropped': _('The webhook call retry jobs have been dropped.'),
'pretix.ssoprovider.created': _('The SSO provider has been created.'),
'pretix.ssoprovider.changed': _('The SSO provider has been changed.'),
'pretix.ssoprovider.deleted': _('The SSO provider has been deleted.'),
'pretix.ssoclient.created': _('The SSO client has been created.'),
'pretix.ssoclient.changed': _('The SSO client has been changed.'),
'pretix.ssoclient.deleted': _('The SSO client has been deleted.'),
'pretix.membershiptype.created': _('The membership type has been created.'),
'pretix.membershiptype.changed': _('The membership type has been changed.'),
'pretix.membershiptype.deleted': _('The membership type has been deleted.'),
'pretix.saleschannel.created': _('The sales channel has been created.'),
'pretix.saleschannel.changed': _('The sales channel has been changed.'),
'pretix.saleschannel.deleted': _('The sales channel has been deleted.'),
'pretix.customer.created': _('The account has been created.'),
'pretix.customer.changed': _('The account has been changed.'),
'pretix.customer.membership.created': _('A membership for this account has been added.'),
'pretix.customer.membership.changed': _('A membership of this account has been changed.'),
'pretix.customer.membership.deleted': _('A membership of this account has been deleted.'),
'pretix.customer.anonymized': _('The account has been disabled and anonymized.'),
'pretix.customer.password.resetrequested': _('A new password has been requested.'),
'pretix.customer.password.set': _('A new password has been set.'),
'pretix.reusable_medium.created': _('The reusable medium has been created.'),
'pretix.reusable_medium.created.auto': _('The reusable medium has been created automatically.'),
'pretix.reusable_medium.changed': _('The reusable medium has been changed.'),
'pretix.reusable_medium.linked_orderposition.changed': _('The medium has been connected to a new ticket.'),
'pretix.reusable_medium.linked_giftcard.changed': _('The medium has been connected to a new gift card.'),
'pretix.email.error': _('Sending of an email has failed.'),
'pretix.event.comment': _('The event\'s internal comment has been updated.'),
'pretix.event.canceled': _('The event has been canceled.'),
'pretix.event.deleted': _('An event has been deleted.'),
'pretix.event.shredder.started': _('A removal process for personal data has been started.'),
'pretix.event.shredder.completed': _('A removal process for personal data has been completed.'),
'pretix.event.order.modified': _('The order details have been changed.'),
'pretix.event.order.unpaid': _('The order has been marked as unpaid.'),
'pretix.event.order.secret.changed': _('The order\'s secret has been changed.'),
'pretix.event.order.expirychanged': _('The order\'s expiry date has been changed.'),
'pretix.event.order.valid_if_pending.set': _('The order has been set to be usable before it is paid.'),
'pretix.event.order.valid_if_pending.unset': _('The order has been set to require payment before use.'),
'pretix.event.order.expired': _('The order has been marked as expired.'),
'pretix.event.order.paid': _('The order has been marked as paid.'),
'pretix.event.order.cancellationrequest.deleted': _('The cancellation request has been deleted.'),
'pretix.event.order.refunded': _('The order has been refunded.'),
'pretix.event.order.reactivated': _('The order has been reactivated.'),
'pretix.event.order.deleted': _('The test mode order {code} has been deleted.'),
'pretix.event.order.placed': _('The order has been created.'),
'pretix.event.order.placed.require_approval': _('The order requires approval before it can continue to be processed.'),
'pretix.event.order.approved': _('The order has been approved.'),
'pretix.event.order.denied': _('The order has been denied (comment: "{comment}").'),
'pretix.event.order.contact.changed': _('The email address has been changed from "{old_email}" '
'to "{new_email}".'),
'pretix.event.order.contact.confirmed': _('The email address has been confirmed to be working (the user clicked on a link '
'in the email for the first time).'),
'pretix.event.order.phone.changed': _('The phone number has been changed from "{old_phone}" '
'to "{new_phone}".'),
'pretix.event.order.customer.changed': _('The customer account has been changed.'),
'pretix.event.order.locale.changed': _('The order locale has been changed.'),
'pretix.event.order.invoice.generated': _('The invoice has been generated.'),
'pretix.event.order.invoice.regenerated': _('The invoice has been regenerated.'),
'pretix.event.order.invoice.reissued': _('The invoice has been reissued.'),
'pretix.event.order.comment': _('The order\'s internal comment has been updated.'),
'pretix.event.order.custom_followup_at': _('The order\'s follow-up date has been updated.'),
'pretix.event.order.checkin_attention': _('The order\'s flag to require attention at check-in has been '
'toggled.'),
'pretix.event.order.checkin_text': _('The order\'s check-in text has been changed.'),
'pretix.event.order.pretix.event.order.valid_if_pending': _('The order\'s flag to be considered valid even if '
'unpaid has been toggled.'),
'pretix.event.order.payment.changed': _('A new payment {local_id} has been started instead of the previous one.'),
'pretix.event.order.email.sent': _('An unidentified type email has been sent.'),
'pretix.event.order.email.error': _('Sending of an email has failed.'),
'pretix.event.order.email.attachments.skipped': _('The email has been sent without attached tickets since they '
'would have been too large to be likely to arrive.'),
'pretix.event.order.email.custom_sent': _('A custom email has been sent.'),
'pretix.event.order.position.email.custom_sent': _('A custom email has been sent to an attendee.'),
'pretix.event.order.email.download_reminder_sent': _('An email has been sent with a reminder that the ticket '
'is available for download.'),
'pretix.event.order.email.expire_warning_sent': _('An email has been sent with a warning that the order is about '
'to expire.'),
'pretix.event.order.email.order_canceled': _('An email has been sent to notify the user that the order has been canceled.'),
'pretix.event.order.email.event_canceled': _('An email has been sent to notify the user that the event has '
'been canceled.'),
'pretix.event.order.email.order_changed': _('An email has been sent to notify the user that the order has been changed.'),
'pretix.event.order.email.order_free': _('An email has been sent to notify the user that the order has been received.'),
'pretix.event.order.email.order_paid': _('An email has been sent to notify the user that payment has been received.'),
'pretix.event.order.email.order_denied': _('An email has been sent to notify the user that the order has been denied.'),
'pretix.event.order.email.order_approved': _('An email has been sent to notify the user that the order has '
'been approved.'),
'pretix.event.order.email.order_placed': _('An email has been sent to notify the user that the order has been received and requires payment.'),
'pretix.event.order.email.order_placed_require_approval': _('An email has been sent to notify the user that '
'the order has been received and requires '
'approval.'),
'pretix.event.order.email.resend': _('An email with a link to the order detail page has been resent to the user.'),
'pretix.event.order.email.payment_failed': _('An email has been sent to notify the user that the payment failed.'),
'pretix.event.order.payment.confirmed': _('Payment {local_id} has been confirmed.'),
'pretix.event.order.payment.canceled': _('Payment {local_id} has been canceled.'),
'pretix.event.order.payment.canceled.failed': _('Canceling payment {local_id} has failed.'),
'pretix.event.order.payment.started': _('Payment {local_id} has been started.'),
'pretix.event.order.payment.failed': _('Payment {local_id} has failed.'),
'pretix.event.order.quotaexceeded': _('The order could not be marked as paid: {message}'),
'pretix.event.order.overpaid': _('The order has been overpaid.'),
'pretix.event.order.refund.created': _('Refund {local_id} has been created.'),
'pretix.event.order.refund.created.externally': _('Refund {local_id} has been created by an external entity.'),
'pretix.event.order.refund.requested': _('The customer requested you to issue a refund.'),
'pretix.event.order.refund.done': _('Refund {local_id} has been completed.'),
'pretix.event.order.refund.canceled': _('Refund {local_id} has been canceled.'),
'pretix.event.order.refund.failed': _('Refund {local_id} has failed.'),
'pretix.event.export.schedule.added': _('A scheduled export has been added.'),
'pretix.event.export.schedule.changed': _('A scheduled export has been changed.'),
'pretix.event.export.schedule.deleted': _('A scheduled export has been deleted.'),
'pretix.event.export.schedule.executed': _('A scheduled export has been executed.'),
'pretix.event.export.schedule.failed': _('A scheduled export has failed: {reason}.'),
'pretix.control.auth.user.created': _('The user has been created.'),
'pretix.control.auth.user.new_source': _('A first login using {agent_type} on {os_type} from {country} has '
'been detected.'),
'pretix.user.settings.2fa.enabled': _('Two-factor authentication has been enabled.'),
'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.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 '
'from your account.'),
'pretix.user.settings.notifications.enabled': _('Notifications have been enabled.'),
'pretix.user.settings.notifications.disabled': _('Notifications have been disabled.'),
'pretix.user.settings.notifications.changed': _('Your notification settings have been changed.'),
'pretix.user.anonymized': _('This user has been anonymized.'),
'pretix.user.oauth.authorized': _('The application "{application_name}" has been authorized to access your '
'account.'),
'pretix.control.auth.user.forgot_password.mail_sent': _('Password reset mail sent.'),
'pretix.control.auth.user.forgot_password.recovered': _('The password has been reset.'),
'pretix.control.auth.user.forgot_password.denied.repeated': _('A repeated password reset has been denied, as '
'the last request was less than 24 hours ago.'),
'pretix.organizer.deleted': _('The organizer "{name}" has been deleted.'),
'pretix.voucher.added': _('The voucher has been created.'),
'pretix.voucher.sent': _('The voucher has been sent to {recipient}.'),
'pretix.voucher.added.waitinglist': _('The voucher has been created and sent to a person on the waiting list.'),
'pretix.voucher.expired.waitinglist': _('The voucher has been set to expire because the recipient removed themselves from the waiting list.'),
'pretix.voucher.changed': _('The voucher has been changed.'),
'pretix.voucher.deleted': _('The voucher has been deleted.'),
'pretix.voucher.redeemed': _('The voucher has been redeemed in order {order_code}.'),
'pretix.event.item.added': _('The product has been created.'),
'pretix.event.item.changed': _('The product has been changed.'),
'pretix.event.item.reordered': _('The product has been reordered.'),
'pretix.event.item.deleted': _('The product has been deleted.'),
'pretix.event.item.variation.added': _('The variation "{value}" has been created.'),
'pretix.event.item.variation.deleted': _('The variation "{value}" has been deleted.'),
'pretix.event.item.variation.changed': _('The variation "{value}" has been changed.'),
'pretix.event.item.addons.added': _('An add-on has been added to this product.'),
'pretix.event.item.addons.removed': _('An add-on has been removed from this product.'),
'pretix.event.item.addons.changed': _('An add-on has been changed on this product.'),
'pretix.event.item.bundles.added': _('A bundled item has been added to this product.'),
'pretix.event.item.bundles.removed': _('A bundled item has been removed from this product.'),
'pretix.event.item.bundles.changed': _('A bundled item has been changed on this product.'),
'pretix.event.item_meta_property.added': _('A meta property has been added to this event.'),
'pretix.event.item_meta_property.deleted': _('A meta property has been removed from this event.'),
'pretix.event.item_meta_property.changed': _('A meta property has been changed on this event.'),
'pretix.event.quota.added': _('The quota has been added.'),
'pretix.event.quota.deleted': _('The quota has been deleted.'),
'pretix.event.quota.changed': _('The quota has been changed.'),
'pretix.event.quota.closed': _('The quota has closed.'),
'pretix.event.quota.opened': _('The quota has been re-opened.'),
'pretix.event.category.added': _('The category has been added.'),
'pretix.event.category.deleted': _('The category has been deleted.'),
'pretix.event.category.changed': _('The category has been changed.'),
'pretix.event.category.reordered': _('The category has been reordered.'),
'pretix.event.question.added': _('The question has been added.'),
'pretix.event.question.deleted': _('The question has been deleted.'),
'pretix.event.question.changed': _('The question has been changed.'),
'pretix.event.question.reordered': _('The question has been reordered.'),
'pretix.event.discount.added': _('The discount has been added.'),
'pretix.event.discount.deleted': _('The discount has been deleted.'),
'pretix.event.discount.changed': _('The discount has been changed.'),
'pretix.event.taxrule.added': _('The tax rule has been added.'),
'pretix.event.taxrule.deleted': _('The tax rule has been deleted.'),
'pretix.event.taxrule.changed': _('The tax rule has been changed.'),
'pretix.event.checkinlist.added': _('The check-in list has been added.'),
'pretix.event.checkinlist.deleted': _('The check-in list has been deleted.'),
'pretix.event.checkinlists.deleted': _('The check-in list has been deleted.'), # backwards compatibility
'pretix.event.checkinlist.changed': _('The check-in list has been changed.'),
'pretix.event.settings': _('The event settings have been changed.'),
'pretix.event.tickets.settings': _('The ticket download settings have been changed.'),
'pretix.event.plugins.enabled': _('A plugin has been enabled.'),
'pretix.event.plugins.disabled': _('A plugin has been disabled.'),
'pretix.event.live.activated': _('The shop has been taken live.'),
'pretix.event.live.deactivated': _('The shop has been taken offline.'),
'pretix.event.testmode.activated': _('The shop has been taken into test mode.'),
'pretix.event.testmode.deactivated': _('The test mode has been disabled.'),
'pretix.event.added': _('The event has been created.'),
'pretix.event.changed': _('The event details have been changed.'),
'pretix.event.footerlinks.changed': _('The footer links have been changed.'),
'pretix.event.question.option.added': _('An answer option has been added to the question.'),
'pretix.event.question.option.deleted': _('An answer option has been removed from the question.'),
'pretix.event.question.option.changed': _('An answer option has been changed.'),
'pretix.event.permissions.added': _('A user has been added to the event team.'),
'pretix.event.permissions.invited': _('A user has been invited to the event team.'),
'pretix.event.permissions.changed': _('A user\'s permissions have been changed.'),
'pretix.event.permissions.deleted': _('A user has been removed from the event team.'),
'pretix.waitinglist.voucher': _('A voucher has been sent to a person on the waiting list.'), # legacy
'pretix.event.orders.waitinglist.voucher_assigned': _('A voucher has been sent to a person on the waiting list.'),
'pretix.event.orders.waitinglist.deleted': _('An entry has been removed from the waiting list.'),
'pretix.event.order.waitinglist.transferred': _('An entry has been transferred to another waiting list.'), # legacy
'pretix.event.orders.waitinglist.changed': _('An entry has been changed on the waiting list.'),
'pretix.event.orders.waitinglist.added': _('An entry has been added to the waiting list.'),
'pretix.team.created': _('The team has been created.'),
'pretix.team.changed': _('The team settings have been changed.'),
'pretix.team.deleted': _('The team has been deleted.'),
'pretix.gate.created': _('The gate has been created.'),
'pretix.gate.changed': _('The gate has been changed.'),
'pretix.gate.deleted': _('The gate has been deleted.'),
'pretix.subevent.deleted': pgettext_lazy('subevent', 'The event date has been deleted.'),
'pretix.subevent.canceled': pgettext_lazy('subevent', 'The event date has been canceled.'),
'pretix.subevent.changed': pgettext_lazy('subevent', 'The event date has been changed.'),
'pretix.subevent.added': pgettext_lazy('subevent', 'The event date has been created.'),
'pretix.subevent.quota.added': pgettext_lazy('subevent', 'A quota has been added to the event date.'),
'pretix.subevent.quota.changed': pgettext_lazy('subevent', 'A quota has been changed on the event date.'),
'pretix.subevent.quota.deleted': pgettext_lazy('subevent', 'A quota has been removed from the event date.'),
'pretix.device.created': _('The device has been created.'),
'pretix.device.changed': _('The device has been changed.'),
'pretix.device.revoked': _('Access of the device has been revoked.'),
'pretix.device.initialized': _('The device has been initialized.'),
'pretix.device.keyroll': _('The access token of the device has been regenerated.'),
'pretix.device.updated': _('The device has notified the server of an hardware or software update.'),
'pretix.giftcards.created': _('The gift card has been created.'),
'pretix.giftcards.modified': _('The gift card has been changed.'),
'pretix.giftcards.transaction.manual': _('A manual transaction has been performed.'),
}
data = json.loads(logentry.data)
if logentry.action_type.startswith('pretix.event.item.variation'):
if 'value' not in data:
# Backwards compatibility
var = ItemVariation.objects.filter(id=data['id']).first()
if var:
data['value'] = str(var.value)
else:
data['value'] = '?'
else:
data['value'] = LazyI18nString(data['value'])
if logentry.action_type == "pretix.voucher.redeemed":
data = defaultdict(lambda: '?', data)
url = reverse('control:event.order', kwargs={
'event': logentry.event.slug,
'organizer': logentry.event.organizer.slug,
'code': data['order_code']
})
return mark_safe(plains[logentry.action_type].format(
order_code='<a href="{}">{}</a>'.format(url, data['order_code']),
))
if logentry.action_type in plains:
data = defaultdict(lambda: '?', data)
return plains[logentry.action_type].format_map(data)
if logentry.action_type.startswith('pretix.event.order.changed'): if logentry.action_type.startswith('pretix.event.order.changed'):
return _display_order_changed(sender, logentry) return _display_order_changed(sender, logentry)
@@ -359,22 +623,91 @@ def pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs):
return _('The order has been canceled.') return _('The order has been canceled.')
if logentry.action_type in ('pretix.control.views.checkin.reverted', 'pretix.event.checkin.reverted'): if logentry.action_type in ('pretix.control.views.checkin.reverted', 'pretix.event.checkin.reverted'):
if 'list' in logentry.parsed_data: if 'list' in data:
try: try:
checkin_list = sender.checkin_lists.get(pk=logentry.parsed_data.get('list')).name checkin_list = sender.checkin_lists.get(pk=data.get('list')).name
except CheckinList.DoesNotExist: except CheckinList.DoesNotExist:
checkin_list = _("(unknown)") checkin_list = _("(unknown)")
else: else:
checkin_list = _("(unknown)") checkin_list = _("(unknown)")
return _('The check-in of position #{posid} on list "{list}" has been reverted.').format( return _('The check-in of position #{posid} on list "{list}" has been reverted.').format(
posid=logentry.parsed_data.get('positionid'), posid=data.get('positionid'),
list=checkin_list, list=checkin_list,
) )
if sender and logentry.action_type.startswith('pretix.event.checkin'): if sender and logentry.action_type.startswith('pretix.event.checkin'):
return _display_checkin(sender, logentry) return _display_checkin(sender, logentry)
if logentry.action_type == 'pretix.control.views.checkin':
# deprecated
dt = dateutil.parser.parse(data.get('datetime'))
tz = sender.timezone
dt_formatted = date_format(dt.astimezone(tz), "SHORT_DATETIME_FORMAT")
if 'list' in data:
try:
checkin_list = sender.checkin_lists.get(pk=data.get('list')).name
except CheckinList.DoesNotExist:
checkin_list = _("(unknown)")
else:
checkin_list = _("(unknown)")
if data.get('first'):
return _('Position #{posid} has been checked in manually at {datetime} on list "{list}".').format(
posid=data.get('positionid'),
datetime=dt_formatted,
list=checkin_list,
)
return _('Position #{posid} has been checked in again at {datetime} on list "{list}".').format(
posid=data.get('positionid'),
datetime=dt_formatted,
list=checkin_list
)
if logentry.action_type == 'pretix.team.member.added':
return _('{user} has been added to the team.').format(user=data.get('email'))
if logentry.action_type == 'pretix.team.member.removed':
return _('{user} has been removed from the team.').format(user=data.get('email'))
if logentry.action_type == 'pretix.team.member.joined':
return _('{user} has joined the team using the invite sent to {email}.').format(
user=data.get('email'), email=data.get('invite_email')
)
if logentry.action_type == 'pretix.team.invite.created':
return _('{user} has been invited to the team.').format(user=data.get('email'))
if logentry.action_type == 'pretix.team.invite.resent':
return _('Invite for {user} has been resent.').format(user=data.get('email'))
if logentry.action_type == 'pretix.team.invite.deleted':
return _('The invite for {user} has been revoked.').format(user=data.get('email'))
if logentry.action_type == 'pretix.team.token.created':
return _('The token "{name}" has been created.').format(name=data.get('name'))
if logentry.action_type == 'pretix.team.token.deleted':
return _('The token "{name}" has been revoked.').format(name=data.get('name'))
if logentry.action_type == 'pretix.user.settings.changed':
text = str(_('Your account settings have been changed.'))
if 'email' in data:
text = text + ' ' + str(_('Your email address has been changed to {email}.').format(email=data['email']))
if 'new_pw' in data:
text = text + ' ' + str(_('Your password has been changed.'))
if data.get('is_active') is True:
text = text + ' ' + str(_('Your account has been enabled.'))
elif data.get('is_active') is False:
text = text + ' ' + str(_('Your account has been disabled.'))
return text
if logentry.action_type == 'pretix.control.auth.user.impersonated':
return str(_('You impersonated {}.')).format(data['other_email'])
if logentry.action_type == 'pretix.control.auth.user.impersonate_stopped':
return str(_('You stopped impersonating {}.')).format(data['other_email'])
@receiver(signal=orderposition_blocked_display, dispatch_uid="pretixcontrol_orderposition_blocked_display") @receiver(signal=orderposition_blocked_display, dispatch_uid="pretixcontrol_orderposition_blocked_display")
def pretixcontrol_orderposition_blocked_display(sender: Event, orderposition, block_name, **kwargs): def pretixcontrol_orderposition_blocked_display(sender: Event, orderposition, block_name, **kwargs):
@@ -382,459 +715,3 @@ def pretixcontrol_orderposition_blocked_display(sender: Event, orderposition, bl
return _('Blocked manually') return _('Blocked manually')
elif block_name.startswith('api:'): elif block_name.startswith('api:'):
return _('Blocked because of an API integration') return _('Blocked because of an API integration')
@log_entry_types.new_from_dict({
'pretix.event.order.modified': _('The order details have been changed.'),
'pretix.event.order.unpaid': _('The order has been marked as unpaid.'),
'pretix.event.order.secret.changed': _('The order\'s secret has been changed.'),
'pretix.event.order.expirychanged': _('The order\'s expiry date has been changed.'),
'pretix.event.order.valid_if_pending.set': _('The order has been set to be usable before it is paid.'),
'pretix.event.order.valid_if_pending.unset': _('The order has been set to require payment before use.'),
'pretix.event.order.expired': _('The order has been marked as expired.'),
'pretix.event.order.paid': _('The order has been marked as paid.'),
'pretix.event.order.cancellationrequest.deleted': _('The cancellation request has been deleted.'),
'pretix.event.order.refunded': _('The order has been refunded.'),
'pretix.event.order.reactivated': _('The order has been reactivated.'),
'pretix.event.order.deleted': _('The test mode order {code} has been deleted.'),
'pretix.event.order.placed': _('The order has been created.'),
'pretix.event.order.placed.require_approval': _(
'The order requires approval before it can continue to be processed.'),
'pretix.event.order.approved': _('The order has been approved.'),
'pretix.event.order.denied': _('The order has been denied (comment: "{comment}").'),
'pretix.event.order.contact.changed': _('The email address has been changed from "{old_email}" '
'to "{new_email}".'),
'pretix.event.order.contact.confirmed': _(
'The email address has been confirmed to be working (the user clicked on a link '
'in the email for the first time).'),
'pretix.event.order.phone.changed': _('The phone number has been changed from "{old_phone}" '
'to "{new_phone}".'),
'pretix.event.order.customer.changed': _('The customer account has been changed.'),
'pretix.event.order.locale.changed': _('The order locale has been changed.'),
'pretix.event.order.invoice.generated': _('The invoice has been generated.'),
'pretix.event.order.invoice.regenerated': _('The invoice has been regenerated.'),
'pretix.event.order.invoice.reissued': _('The invoice has been reissued.'),
'pretix.event.order.comment': _('The order\'s internal comment has been updated.'),
'pretix.event.order.custom_followup_at': _('The order\'s follow-up date has been updated.'),
'pretix.event.order.checkin_attention': _('The order\'s flag to require attention at check-in has been '
'toggled.'),
'pretix.event.order.checkin_text': _('The order\'s check-in text has been changed.'),
'pretix.event.order.pretix.event.order.valid_if_pending': _('The order\'s flag to be considered valid even if '
'unpaid has been toggled.'),
'pretix.event.order.payment.changed': _('A new payment {local_id} has been started instead of the previous one.'),
'pretix.event.order.email.sent': _('An unidentified type email has been sent.'),
'pretix.event.order.email.error': _('Sending of an email has failed.'),
'pretix.event.order.email.attachments.skipped': _('The email has been sent without attached tickets since they '
'would have been too large to be likely to arrive.'),
'pretix.event.order.email.custom_sent': _('A custom email has been sent.'),
'pretix.event.order.position.email.custom_sent': _('A custom email has been sent to an attendee.'),
'pretix.event.order.email.download_reminder_sent': _('An email has been sent with a reminder that the ticket '
'is available for download.'),
'pretix.event.order.email.expire_warning_sent': _('An email has been sent with a warning that the order is about '
'to expire.'),
'pretix.event.order.email.order_canceled': _(
'An email has been sent to notify the user that the order has been canceled.'),
'pretix.event.order.email.event_canceled': _('An email has been sent to notify the user that the event has '
'been canceled.'),
'pretix.event.order.email.order_changed': _(
'An email has been sent to notify the user that the order has been changed.'),
'pretix.event.order.email.order_free': _(
'An email has been sent to notify the user that the order has been received.'),
'pretix.event.order.email.order_paid': _(
'An email has been sent to notify the user that payment has been received.'),
'pretix.event.order.email.order_denied': _(
'An email has been sent to notify the user that the order has been denied.'),
'pretix.event.order.email.order_approved': _('An email has been sent to notify the user that the order has '
'been approved.'),
'pretix.event.order.email.order_placed': _(
'An email has been sent to notify the user that the order has been received and requires payment.'),
'pretix.event.order.email.order_placed_require_approval': _('An email has been sent to notify the user that '
'the order has been received and requires '
'approval.'),
'pretix.event.order.email.resend': _('An email with a link to the order detail page has been resent to the user.'),
'pretix.event.order.email.payment_failed': _('An email has been sent to notify the user that the payment failed.'),
})
class CoreOrderLogEntryType(OrderLogEntryType):
pass
@log_entry_types.new_from_dict({
'pretix.voucher.added': _('The voucher has been created.'),
'pretix.voucher.sent': _('The voucher has been sent to {recipient}.'),
'pretix.voucher.added.waitinglist': _('The voucher has been created and sent to a person on the waiting list.'),
'pretix.voucher.expired.waitinglist': _(
'The voucher has been set to expire because the recipient removed themselves from the waiting list.'),
'pretix.voucher.changed': _('The voucher has been changed.'),
'pretix.voucher.deleted': _('The voucher has been deleted.'),
})
class CoreVoucherLogEntryType(VoucherLogEntryType):
pass
@log_entry_types.new()
class VoucherRedeemedLogEntryType(VoucherLogEntryType):
action_type = 'pretix.voucher.redeemed'
plain = _('The voucher has been redeemed in order {order_code}.')
def display(self, logentry):
data = json.loads(logentry.data)
data = defaultdict(lambda: '?', data)
url = reverse('control:event.order', kwargs={
'event': logentry.event.slug,
'organizer': logentry.event.organizer.slug,
'code': data['order_code']
})
return mark_safe(self.plain.format(
order_code='<a href="{}">{}</a>'.format(url, data['order_code']),
))
@log_entry_types.new_from_dict({
'pretix.event.category.added': _('The category has been added.'),
'pretix.event.category.deleted': _('The category has been deleted.'),
'pretix.event.category.changed': _('The category has been changed.'),
'pretix.event.category.reordered': _('The category has been reordered.'),
})
class CoreItemCategoryLogEntryType(ItemCategoryLogEntryType):
pass
@log_entry_types.new_from_dict({
'pretix.event.taxrule.added': _('The tax rule has been added.'),
'pretix.event.taxrule.deleted': _('The tax rule has been deleted.'),
'pretix.event.taxrule.changed': _('The tax rule has been changed.'),
})
class CoreTaxRuleLogEntryType(TaxRuleLogEntryType):
pass
class TeamMembershipLogEntryType(LogEntryType):
def display(self, logentry):
return self.plain.format(user=logentry.parsed_data.get('email'))
@log_entry_types.new_from_dict({
'pretix.team.member.added': _('{user} has been added to the team.'),
'pretix.team.member.removed': _('{user} has been removed from the team.'),
'pretix.team.invite.created': _('{user} has been invited to the team.'),
'pretix.team.invite.resent': _('Invite for {user} has been resent.'),
})
class CoreTeamMembershipLogEntryType(TeamMembershipLogEntryType):
pass
@log_entry_types.new()
class TeamMemberJoinedLogEntryType(LogEntryType):
action_type = 'pretix.team.member.joined'
def display(self, logentry):
return _('{user} has joined the team using the invite sent to {email}.').format(
user=logentry.parsed_data.get('email'), email=logentry.parsed_data.get('invite_email')
)
@log_entry_types.new()
class UserSettingsChangedLogEntryType(LogEntryType):
action_type = 'pretix.user.settings.changed'
def display(self, logentry):
text = str(_('Your account settings have been changed.'))
if 'email' in logentry.parsed_data:
text = text + ' ' + str(
_('Your email address has been changed to {email}.').format(email=logentry.parsed_data['email']))
if 'new_pw' in logentry.parsed_data:
text = text + ' ' + str(_('Your password has been changed.'))
if logentry.parsed_data.get('is_active') is True:
text = text + ' ' + str(_('Your account has been enabled.'))
elif logentry.parsed_data.get('is_active') is False:
text = text + ' ' + str(_('Your account has been disabled.'))
return text
class UserImpersonatedLogEntryType(LogEntryType):
def display(self, logentry):
return self.plain.format(logentry.parsed_data['other_email'])
@log_entry_types.new_from_dict({
'pretix.control.auth.user.impersonated': _('You impersonated {}.'),
'pretix.control.auth.user.impersonate_stopped': _('You stopped impersonating {}.'),
})
class CoreUserImpersonatedLogEntryType(UserImpersonatedLogEntryType):
pass
@log_entry_types.new_from_dict({
'pretix.object.cloned': _('This object has been created by cloning.'),
'pretix.organizer.changed': _('The organizer has been changed.'),
'pretix.organizer.settings': _('The organizer settings have been changed.'),
'pretix.organizer.footerlinks.changed': _('The footer links have been changed.'),
'pretix.organizer.export.schedule.added': _('A scheduled export has been added.'),
'pretix.organizer.export.schedule.changed': _('A scheduled export has been changed.'),
'pretix.organizer.export.schedule.deleted': _('A scheduled export has been deleted.'),
'pretix.organizer.export.schedule.executed': _('A scheduled export has been executed.'),
'pretix.organizer.export.schedule.failed': _('A scheduled export has failed: {reason}.'),
'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.'),
'pretix.giftcards.acceptance.acceptor.removed': _('A gift card acceptor has been removed.'),
'pretix.giftcards.acceptance.issuer.removed': _('A gift card issuer has been removed or declined.'),
'pretix.giftcards.acceptance.issuer.accepted': _('A new gift card issuer has been accepted.'),
'pretix.webhook.created': _('The webhook has been created.'),
'pretix.webhook.changed': _('The webhook has been changed.'),
'pretix.webhook.retries.expedited': _('The webhook call retry jobs have been manually expedited.'),
'pretix.webhook.retries.dropped': _('The webhook call retry jobs have been dropped.'),
'pretix.ssoprovider.created': _('The SSO provider has been created.'),
'pretix.ssoprovider.changed': _('The SSO provider has been changed.'),
'pretix.ssoprovider.deleted': _('The SSO provider has been deleted.'),
'pretix.ssoclient.created': _('The SSO client has been created.'),
'pretix.ssoclient.changed': _('The SSO client has been changed.'),
'pretix.ssoclient.deleted': _('The SSO client has been deleted.'),
'pretix.membershiptype.created': _('The membership type has been created.'),
'pretix.membershiptype.changed': _('The membership type has been changed.'),
'pretix.membershiptype.deleted': _('The membership type has been deleted.'),
'pretix.saleschannel.created': _('The sales channel has been created.'),
'pretix.saleschannel.changed': _('The sales channel has been changed.'),
'pretix.saleschannel.deleted': _('The sales channel has been deleted.'),
'pretix.customer.created': _('The account has been created.'),
'pretix.customer.changed': _('The account has been changed.'),
'pretix.customer.membership.created': _('A membership for this account has been added.'),
'pretix.customer.membership.changed': _('A membership of this account has been changed.'),
'pretix.customer.membership.deleted': _('A membership of this account has been deleted.'),
'pretix.customer.anonymized': _('The account has been disabled and anonymized.'),
'pretix.customer.password.resetrequested': _('A new password has been requested.'),
'pretix.customer.password.set': _('A new password has been set.'),
'pretix.reusable_medium.created': _('The reusable medium has been created.'),
'pretix.reusable_medium.created.auto': _('The reusable medium has been created automatically.'),
'pretix.reusable_medium.changed': _('The reusable medium has been changed.'),
'pretix.reusable_medium.linked_orderposition.changed': _('The medium has been connected to a new ticket.'),
'pretix.reusable_medium.linked_giftcard.changed': _('The medium has been connected to a new gift card.'),
'pretix.email.error': _('Sending of an email has failed.'),
'pretix.event.comment': _('The event\'s internal comment has been updated.'),
'pretix.event.canceled': _('The event has been canceled.'),
'pretix.event.deleted': _('An event has been deleted.'),
'pretix.event.shredder.started': _('A removal process for personal data has been started.'),
'pretix.event.shredder.completed': _('A removal process for personal data has been completed.'),
'pretix.event.export.schedule.added': _('A scheduled export has been added.'),
'pretix.event.export.schedule.changed': _('A scheduled export has been changed.'),
'pretix.event.export.schedule.deleted': _('A scheduled export has been deleted.'),
'pretix.event.export.schedule.executed': _('A scheduled export has been executed.'),
'pretix.event.export.schedule.failed': _('A scheduled export has failed: {reason}.'),
'pretix.control.auth.user.created': _('The user has been created.'),
'pretix.control.auth.user.new_source': _('A first login using {agent_type} on {os_type} from {country} has '
'been detected.'),
'pretix.user.settings.2fa.enabled': _('Two-factor authentication has been enabled.'),
'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.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 '
'from your account.'),
'pretix.user.settings.notifications.enabled': _('Notifications have been enabled.'),
'pretix.user.settings.notifications.disabled': _('Notifications have been disabled.'),
'pretix.user.settings.notifications.changed': _('Your notification settings have been changed.'),
'pretix.user.anonymized': _('This user has been anonymized.'),
'pretix.user.oauth.authorized': _('The application "{application_name}" has been authorized to access your '
'account.'),
'pretix.control.auth.user.forgot_password.mail_sent': _('Password reset mail sent.'),
'pretix.control.auth.user.forgot_password.recovered': _('The password has been reset.'),
'pretix.control.auth.user.forgot_password.denied.repeated': _('A repeated password reset has been denied, as '
'the last request was less than 24 hours ago.'),
'pretix.organizer.deleted': _('The organizer "{name}" has been deleted.'),
'pretix.waitinglist.voucher': _('A voucher has been sent to a person on the waiting list.'), # legacy
'pretix.event.orders.waitinglist.voucher_assigned': _('A voucher has been sent to a person on the waiting list.'),
'pretix.event.orders.waitinglist.deleted': _('An entry has been removed from the waiting list.'),
'pretix.event.order.waitinglist.transferred': _('An entry has been transferred to another waiting list.'), # legacy
'pretix.event.orders.waitinglist.changed': _('An entry has been changed on the waiting list.'),
'pretix.event.orders.waitinglist.added': _('An entry has been added to the waiting list.'),
'pretix.team.created': _('The team has been created.'),
'pretix.team.changed': _('The team settings have been changed.'),
'pretix.team.deleted': _('The team has been deleted.'),
'pretix.gate.created': _('The gate has been created.'),
'pretix.gate.changed': _('The gate has been changed.'),
'pretix.gate.deleted': _('The gate has been deleted.'),
'pretix.subevent.deleted': pgettext_lazy('subevent', 'The event date has been deleted.'),
'pretix.subevent.canceled': pgettext_lazy('subevent', 'The event date has been canceled.'),
'pretix.subevent.changed': pgettext_lazy('subevent', 'The event date has been changed.'),
'pretix.subevent.added': pgettext_lazy('subevent', 'The event date has been created.'),
'pretix.subevent.quota.added': pgettext_lazy('subevent', 'A quota has been added to the event date.'),
'pretix.subevent.quota.changed': pgettext_lazy('subevent', 'A quota has been changed on the event date.'),
'pretix.subevent.quota.deleted': pgettext_lazy('subevent', 'A quota has been removed from the event date.'),
'pretix.device.created': _('The device has been created.'),
'pretix.device.changed': _('The device has been changed.'),
'pretix.device.revoked': _('Access of the device has been revoked.'),
'pretix.device.initialized': _('The device has been initialized.'),
'pretix.device.keyroll': _('The access token of the device has been regenerated.'),
'pretix.device.updated': _('The device has notified the server of an hardware or software update.'),
'pretix.giftcards.created': _('The gift card has been created.'),
'pretix.giftcards.modified': _('The gift card has been changed.'),
'pretix.giftcards.transaction.manual': _('A manual transaction has been performed.'),
'pretix.team.token.created': _('The token "{name}" has been created.'),
'pretix.team.token.deleted': _('The token "{name}" has been revoked.'),
})
class CoreLogEntryType(LogEntryType):
pass
@log_entry_types.new_from_dict({
'pretix.event.item_meta_property.added': _('A meta property has been added to this event.'),
'pretix.event.item_meta_property.deleted': _('A meta property has been removed from this event.'),
'pretix.event.item_meta_property.changed': _('A meta property has been changed on this event.'),
'pretix.event.checkinlist.added': _('The check-in list has been added.'),
'pretix.event.checkinlist.deleted': _('The check-in list has been deleted.'),
'pretix.event.checkinlists.deleted': _('The check-in list has been deleted.'), # backwards compatibility
'pretix.event.checkinlist.changed': _('The check-in list has been changed.'),
'pretix.event.settings': _('The event settings have been changed.'),
'pretix.event.tickets.settings': _('The ticket download settings have been changed.'),
'pretix.event.live.activated': _('The shop has been taken live.'),
'pretix.event.live.deactivated': _('The shop has been taken offline.'),
'pretix.event.testmode.activated': _('The shop has been taken into test mode.'),
'pretix.event.testmode.deactivated': _('The test mode has been disabled.'),
'pretix.event.added': _('The event has been created.'),
'pretix.event.changed': _('The event details have been changed.'),
'pretix.event.footerlinks.changed': _('The footer links have been changed.'),
'pretix.event.question.option.added': _('An answer option has been added to the question.'),
'pretix.event.question.option.deleted': _('An answer option has been removed from the question.'),
'pretix.event.question.option.changed': _('An answer option has been changed.'),
'pretix.event.permissions.added': _('A user has been added to the event team.'),
'pretix.event.permissions.invited': _('A user has been invited to the event team.'),
'pretix.event.permissions.changed': _('A user\'s permissions have been changed.'),
'pretix.event.permissions.deleted': _('A user has been removed from the event team.'),
})
class CoreEventLogEntryType(EventLogEntryType):
pass
@log_entry_types.new_from_dict({
'pretix.event.plugins.enabled': _('The plugin has been enabled.'),
'pretix.event.plugins.disabled': _('The plugin has been disabled.'),
})
class EventPluginStateLogEntryType(EventLogEntryType):
object_link_wrapper = _('Plugin {val}')
def get_object_link_info(self, logentry) -> dict:
if 'plugin' in logentry.parsed_data:
app = app_cache.get(logentry.parsed_data['plugin'])
if app and hasattr(app, 'PretixPluginMeta'):
return {
'href': reverse('control:event.settings.plugins', kwargs={
'organizer': logentry.event.organizer.slug,
'event': logentry.event.slug,
}) + '#plugin_' + logentry.parsed_data['plugin'],
'val': app.PretixPluginMeta.name
}
@log_entry_types.new_from_dict({
'pretix.event.item.added': _('The product has been created.'),
'pretix.event.item.changed': _('The product has been changed.'),
'pretix.event.item.reordered': _('The product has been reordered.'),
'pretix.event.item.deleted': _('The product has been deleted.'),
'pretix.event.item.addons.added': _('An add-on has been added to this product.'),
'pretix.event.item.addons.removed': _('An add-on has been removed from this product.'),
'pretix.event.item.addons.changed': _('An add-on has been changed on this product.'),
'pretix.event.item.bundles.added': _('A bundled item has been added to this product.'),
'pretix.event.item.bundles.removed': _('A bundled item has been removed from this product.'),
'pretix.event.item.bundles.changed': _('A bundled item has been changed on this product.'),
})
class CoreItemLogEntryType(ItemLogEntryType):
pass
@log_entry_types.new_from_dict({
'pretix.event.item.variation.added': _('The variation "{value}" has been created.'),
'pretix.event.item.variation.deleted': _('The variation "{value}" has been deleted.'),
'pretix.event.item.variation.changed': _('The variation "{value}" has been changed.'),
})
class VariationLogEntryType(ItemLogEntryType):
def display(self, logentry):
if 'value' not in logentry.parsed_data:
# Backwards compatibility
var = ItemVariation.objects.filter(id=logentry.parsed_data['id']).first()
if var:
logentry.parsed_data['value'] = str(var.value)
else:
logentry.parsed_data['value'] = '?'
else:
logentry.parsed_data['value'] = LazyI18nString(logentry.parsed_data['value'])
return super().display(logentry)
@log_entry_types.new_from_dict({
'pretix.event.order.payment.confirmed': _('Payment {local_id} has been confirmed.'),
'pretix.event.order.payment.canceled': _('Payment {local_id} has been canceled.'),
'pretix.event.order.payment.canceled.failed': _('Canceling payment {local_id} has failed.'),
'pretix.event.order.payment.started': _('Payment {local_id} has been started.'),
'pretix.event.order.payment.failed': _('Payment {local_id} has failed.'),
'pretix.event.order.quotaexceeded': _('The order could not be marked as paid: {message}'),
'pretix.event.order.overpaid': _('The order has been overpaid.'),
'pretix.event.order.refund.created': _('Refund {local_id} has been created.'),
'pretix.event.order.refund.created.externally': _('Refund {local_id} has been created by an external entity.'),
'pretix.event.order.refund.requested': _('The customer requested you to issue a refund.'),
'pretix.event.order.refund.done': _('Refund {local_id} has been completed.'),
'pretix.event.order.refund.canceled': _('Refund {local_id} has been canceled.'),
'pretix.event.order.refund.failed': _('Refund {local_id} has failed.'),
})
class CoreOrderPaymentLogEntryType(OrderLogEntryType):
pass
@log_entry_types.new_from_dict({
'pretix.event.quota.added': _('The quota has been added.'),
'pretix.event.quota.deleted': _('The quota has been deleted.'),
'pretix.event.quota.changed': _('The quota has been changed.'),
'pretix.event.quota.closed': _('The quota has closed.'),
'pretix.event.quota.opened': _('The quota has been re-opened.'),
})
class CoreQuotaLogEntryType(QuotaLogEntryType):
pass
@log_entry_types.new_from_dict({
'pretix.event.question.added': _('The question has been added.'),
'pretix.event.question.deleted': _('The question has been deleted.'),
'pretix.event.question.changed': _('The question has been changed.'),
'pretix.event.question.reordered': _('The question has been reordered.'),
})
class CoreQuestionLogEntryType(QuestionLogEntryType):
pass
@log_entry_types.new_from_dict({
'pretix.event.discount.added': _('The discount has been added.'),
'pretix.event.discount.deleted': _('The discount has been deleted.'),
'pretix.event.discount.changed': _('The discount has been changed.'),
})
class CoreDiscountLogEntryType(DiscountLogEntryType):
pass
@log_entry_types.new()
class LegacyCheckinLogEntryType(OrderLogEntryType):
action_type = 'pretix.control.views.checkin'
def display(self, logentry):
# deprecated
dt = dateutil.parser.parse(logentry.parsed_data.get('datetime'))
tz = logentry.event.timezone
dt_formatted = date_format(dt.astimezone(tz), "SHORT_DATETIME_FORMAT")
if 'list' in logentry.parsed_data:
try:
checkin_list = logentry.event.checkin_lists.get(pk=logentry.parsed_data.get('list')).name
except CheckinList.DoesNotExist:
checkin_list = _("(unknown)")
else:
checkin_list = _("(unknown)")
if logentry.parsed_data.get('first'):
return _('Position #{posid} has been checked in manually at {datetime} on list "{list}".').format(
posid=logentry.parsed_data.get('positionid'),
datetime=dt_formatted,
list=checkin_list,
)
return _('Position #{posid} has been checked in again at {datetime} on list "{list}".').format(
posid=logentry.parsed_data.get('positionid'),
datetime=dt_formatted,
list=checkin_list
)
+2 -2
View File
@@ -34,7 +34,7 @@
from django.dispatch import Signal from django.dispatch import Signal
from pretix.base.signals import DeprecatedSignal, EventPluginSignal, OrganizerPluginSignal from pretix.base.signals import DeprecatedSignal, EventPluginSignal
html_page_start = Signal() html_page_start = Signal()
""" """
@@ -221,7 +221,7 @@ Deprecated signal, no longer works. We just keep the definition so old plugins d
break the installation. break the installation.
""" """
nav_organizer = OrganizerPluginSignal() nav_organizer = Signal()
""" """
Arguments: 'organizer', 'request' Arguments: 'organizer', 'request'
@@ -23,7 +23,7 @@
<legend>{{ catlabel }}</legend> <legend>{{ catlabel }}</legend>
<div class="plugin-list"> <div class="plugin-list">
{% for plugin in plist %} {% for plugin in plist %}
<div class="plugin-container {% if plugin.featured %}featured-plugin{% endif %}" id="plugin_{{ plugin.module }}"> <div class="plugin-container {% if plugin.featured %}featured-plugin{% endif %}">
{% if plugin.featured %} {% if plugin.featured %}
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-body"> <div class="panel-body">
@@ -372,19 +372,6 @@
</div> </div>
{% bootstrap_field sform.waiting_list_enabled layout="control" %} {% bootstrap_field sform.waiting_list_enabled layout="control" %}
{% bootstrap_field sform.waiting_list_auto layout="control" %} {% bootstrap_field sform.waiting_list_auto layout="control" %}
<div class="form-group">
<label class="control-label col-md-3">
{% trans "Waiting customers" %}
</label>
<div class="col-md-9 static-form-row">
<p>
<a href="{% url "control:event.orders.waitinglist" event=request.event.slug organizer=request.organizer.slug %}#tab-0-1-open"
target="_blank">
{% trans "Manage waiting list" %}
</a>
</p>
</div>
</div>
{% bootstrap_field sform.waiting_list_hours layout="control" %} {% bootstrap_field sform.waiting_list_hours layout="control" %}
{% bootstrap_field sform.waiting_list_auto_disable layout="control" %} {% bootstrap_field sform.waiting_list_auto_disable layout="control" %}
{% bootstrap_field sform.waiting_list_names_asked_required layout="control" %} {% bootstrap_field sform.waiting_list_names_asked_required layout="control" %}
@@ -151,12 +151,6 @@
</div> </div>
<div class="col-sm-4"> <div class="col-sm-4">
{% bootstrap_field position.form.itemvar layout='inline' %} {% bootstrap_field position.form.itemvar layout='inline' %}
{% if position.granted_memberships.all %}
<span class="text-muted">
<span class="fa fa-warning text-warning" aria-hidden="true"></span>
{% trans "The sale of this position created a membership. Changing the product here will not affect the membership. Memberships can be managed in the customer account." %}
</span>
{% endif %}
</div> </div>
</div> </div>
@@ -260,12 +254,6 @@
{% trans "" %} {% trans "" %}
</div> </div>
{% bootstrap_field position.form.valid_until layout='inline' %} {% bootstrap_field position.form.valid_until layout='inline' %}
{% if position.granted_memberships.all %}
<span class="text-muted">
<span class="fa fa-warning text-warning" aria-hidden="true"></span>
{% trans "The sale of this position created a membership. Changing the validity of the ticket here will not affect the membership. Memberships can be managed in the customer account." %}
</span>
{% endif %}
</div> </div>
</div> </div>
@@ -102,24 +102,16 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for t in tokens %} {% for t in team.active_tokens %}
<tr> <tr>
<td {% if not t.active %}class="text-muted"{% endif %}> <td>
{% if not t.active %}
<del>
{% endif %}
{{ t.name }} {{ t.name }}
{% if not t.active %}
</del>
{% endif %}
</td> </td>
<td class="text-right flip"> <td class="text-right flip">
{% if t.active %} <button type="submit" name="remove-token" value="{{ t.id }}"
<button type="submit" name="remove-token" value="{{ t.id }}" class="btn btn-danger btn-sm btn-block">
class="btn btn-danger btn-sm btn-block"> <i class="fa fa-times"></i> {% trans "Remove" %}
<i class="fa fa-times"></i> {% trans "Remove" %} </button>
</button>
{% endif %}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
@@ -177,7 +177,7 @@
{% if name %} {% if name %}
<div class="row control-group pdf-info"> <div class="row control-group pdf-info">
<div class="col-sm-12"> <div class="col-sm-12">
<label for="pdf-info-name">{% trans "Layout name" %}</label><br> <label>{% trans "Layout name" %}</label><br>
<input type="text" id="pdf-info-name" class="input-block-level form-control" name="name" value="{{ name }}"> <input type="text" id="pdf-info-name" class="input-block-level form-control" name="name" value="{{ name }}">
</div> </div>
</div> </div>
@@ -185,11 +185,11 @@
<div class="row control-group pdf-info"> <div class="row control-group pdf-info">
<hr/> <hr/>
<div class="col-sm-6"> <div class="col-sm-6">
<label for="pdf-info-width">{% trans "Width (mm)" %}</label><br> <label>{% trans "Width (mm)" %}</label><br>
<input type="number" id="pdf-info-width" class="input-block-level form-control"> <input type="number" id="pdf-info-width" class="input-block-level form-control">
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<label for="pdf-info-height">{% trans "Height (mm)" %}</label><br> <label>{% trans "Height (mm)" %}</label><br>
<input type="number" id="pdf-info-height" class="input-block-level form-control"> <input type="number" id="pdf-info-height" class="input-block-level form-control">
</div> </div>
</div> </div>
@@ -227,7 +227,7 @@
<div class="row control-group pdf-info"> <div class="row control-group pdf-info">
<hr/> <hr/>
<div class="col-sm-12"> <div class="col-sm-12">
<label for="pdf-info-locale">{% trans "Preferred language" %}</label><br> <label>{% trans "Preferred language" %}</label><br>
<select class="form-control" id="pdf-info-locale"> <select class="form-control" id="pdf-info-locale">
<option value="">{% trans "Order locale" %}</option> <option value="">{% trans "Order locale" %}</option>
{% for l in locales %} {% for l in locales %}
@@ -238,7 +238,7 @@
</div> </div>
<div class="row control-group poweredby"> <div class="row control-group poweredby">
<div class="col-sm-12"> <div class="col-sm-12">
<label for="toolbox-poweredby-style">{% trans "Style" %}</label><br> <label>{% trans "Style" %}</label><br>
<select class="input-block-level form-control" id="toolbox-poweredby-style"> <select class="input-block-level form-control" id="toolbox-poweredby-style">
<option value="dark">{% trans "Dark" %}</option> <option value="dark">{% trans "Dark" %}</option>
<option value="white">{% trans "Light" %}</option> <option value="white">{% trans "Light" %}</option>
@@ -247,7 +247,7 @@
</div> </div>
<div class="row control-group imagecontent"> <div class="row control-group imagecontent">
<div class="col-sm-12"> <div class="col-sm-12">
<label for="toolbox-imagecontent">{% trans "Image content" %}</label><br> <label>{% trans "Image content" %}</label><br>
<select class="input-block-level form-control" id="toolbox-imagecontent"> <select class="input-block-level form-control" id="toolbox-imagecontent">
<option value="">{% trans "Empty" %}</option> <option value="">{% trans "Empty" %}</option>
{% for varname, var in images.items %} {% for varname, var in images.items %}
@@ -258,7 +258,7 @@
</div> </div>
<div class="row control-group text textcontent"> <div class="row control-group text textcontent">
<div class="col-sm-12"> <div class="col-sm-12">
<label for="toolbox-content">{% trans "Content" %}</label><br> <label>{% trans "Content" %}</label><br>
<select class="input-block-level form-control" id="toolbox-content"> <select class="input-block-level form-control" id="toolbox-content">
{% for varname, var in variables.items %} {% for varname, var in variables.items %}
{% if not var.hidden %} {% if not var.hidden %}
@@ -293,31 +293,31 @@
<div class="row control-group position"> <div class="row control-group position">
<hr/> <hr/>
<div class="col-sm-6"> <div class="col-sm-6">
<label for="toolbox-position-x">{% trans "x (mm)" %}</label><br> <label>{% trans "x (mm)" %}</label><br>
<input type="number" value="13" class="input-block-level form-control" step="0.01" <input type="number" value="13" class="input-block-level form-control" step="0.01"
id="toolbox-position-x"> id="toolbox-position-x">
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<label for="toolbox-position-y">{% trans "y (mm)" %}</label><br> <label>{% trans "y (mm)" %}</label><br>
<input type="number" value="13" class="input-block-level form-control" step="0.01" <input type="number" value="13" class="input-block-level form-control" step="0.01"
id="toolbox-position-y"> id="toolbox-position-y">
</div> </div>
</div> </div>
<div class="row control-group rectsize"> <div class="row control-group rectsize">
<div class="col-sm-6"> <div class="col-sm-6">
<label for="toolbox-width">{% trans "Width (mm)" %}</label><br> <label>{% trans "Width (mm)" %}</label><br>
<input type="number" value="13" class="input-block-level form-control" step="0.01" <input type="number" value="13" class="input-block-level form-control" step="0.01"
id="toolbox-width"> id="toolbox-width">
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<label for="toolbox-height">{% trans "Height (mm)" %}</label><br> <label>{% trans "Height (mm)" %}</label><br>
<input type="number" value="13" class="input-block-level form-control" step="0.01" <input type="number" value="13" class="input-block-level form-control" step="0.01"
id="toolbox-height"> id="toolbox-height">
</div> </div>
</div> </div>
<div class="row control-group squaresize poweredby"> <div class="row control-group squaresize poweredby">
<div class="col-sm-12"> <div class="col-sm-12">
<label for="toolbox-squaresize">{% trans "Size (mm)" %}</label><br> <label>{% trans "Size (mm)" %}</label><br>
<input type="number" value="13" class="input-block-level form-control" step="0.01" <input type="number" value="13" class="input-block-level form-control" step="0.01"
id="toolbox-squaresize"> id="toolbox-squaresize">
</div> </div>
@@ -335,13 +335,13 @@
</div> </div>
</div> </div>
<div class="row control-group text"> <div class="row control-group text">
<div class="col-sm-6 textarea"> <div class="col-sm-6">
<label for="toolbox-textwidth">{% trans "Width (mm)" %}</label><br> <label>{% trans "Width (mm)" %}</label><br>
<input type="number" value="13" class="input-block-level form-control" step="0.01" <input type="number" value="13" class="input-block-level form-control" step="0.01"
id="toolbox-textwidth"> id="toolbox-textwidth">
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<label for="toolbox-textrotation">{% trans "Rotation (°)" %}</label><br> <label>{% trans "Rotation (°)" %}</label><br>
<input type="number" value="0" class="input-block-level form-control" step="0.1" <input type="number" value="0" class="input-block-level form-control" step="0.1"
id="toolbox-textrotation"> id="toolbox-textrotation">
</div> </div>
@@ -349,7 +349,7 @@
<div class="row control-group text"> <div class="row control-group text">
<hr/> <hr/>
<div class="col-sm-12"> <div class="col-sm-12">
<label for="toolbox-fontfamily">{% trans "Font" %}</label><br> <label>{% trans "Font" %}</label><br>
<select class="input-block-level form-control" id="toolbox-fontfamily"> <select class="input-block-level form-control" id="toolbox-fontfamily">
<option>Open Sans</option> <option>Open Sans</option>
{% for family in fonts.keys %} {% for family in fonts.keys %}
@@ -360,50 +360,43 @@
</div> </div>
<div class="row control-group text"> <div class="row control-group text">
<div class="col-sm-6"> <div class="col-sm-6">
<label for="toolbox-fontsize">{% trans "Font size (pt)" %}</label><br> <label>{% trans "Font size (pt)" %}</label><br>
<input type="number" value="13" class="input-block-level form-control" step="0.1" <input type="number" value="13" class="input-block-level form-control" step="0.1"
id="toolbox-fontsize"> id="toolbox-fontsize">
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<label for="toolbox-lineheight">{% trans "Line height" %}</label><br> <label>{% trans "Line height" %}</label><br>
<input type="number" value="1" class="input-block-level form-control" step="0.1" <input type="number" value="1" class="input-block-level form-control" step="0.1"
id="toolbox-lineheight"> id="toolbox-lineheight">
</div> </div>
</div> </div>
<div class="row control-group text"> <div class="row control-group text">
<div class="col-sm-6"> <div class="col-sm-6">
<label for="toolbox-col">{% trans "Text color" %}</label><br> <label>{% trans "Text color" %}</label><br>
<div class="input-group"> <input type="text" value="#000000" class="input-block-level form-control colorpickerfield"
<input type="text" value="#000000" class="input-block-level form-control colorpickerfield" id="toolbox-col">
id="toolbox-col">
<span class="input-group-addon contrast-icon">
</span>
</div>
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<label>&nbsp;</label><br> <label>&nbsp;</label><br>
<div class="btn-group btn-group-justified" role="group"> <div class="btn-group btn-group-justified" role="group">
<div class="btn-group text" role="group"> <div class="btn-group" role="group">
<button type="button" class="btn btn-default toggling" data-action="bold"> <button type="button" class="btn btn-default toggling" data-action="bold">
<span class="fa fa-bold"></span> <span class="fa fa-bold"></span>
</button> </button>
</div> </div>
<div class="btn-group text" role="group"> <div class="btn-group" role="group">
<button type="button" class="btn btn-default toggling" data-action="italic"> <button type="button" class="btn btn-default toggling" data-action="italic">
<span class="fa fa-italic"></span> <span class="fa fa-italic"></span>
</button> </button>
</div> </div>
<div class="btn-group textarea" role="group"> <div class="btn-group" role="group">
<button type="button" class="btn btn-default toggling" data-action="downward" <button type="button" class="btn btn-default toggling" data-action="downward"
data-toggle="tooltip" title="{% trans "Flow multiple lines downward from specified position" %}"> data-toggle="tooltip" title="{% trans "Flow multiple lines downward from specified position" %}">
<span class="fa fa-caret-square-o-down"></span> <span class="fa fa-caret-square-o-down"></span>
</button> </button>
</div> </div>
</div> </div>
</div> <label>&nbsp;</label><br>
</div>
<div class="row control-group text">
<div class="col-sm-6">
<div class="btn-group btn-group-justified" id="toolbox-align"> <div class="btn-group btn-group-justified" id="toolbox-align">
<div class="btn-group" role="group"> <div class="btn-group" role="group">
<button type="button" class="btn btn-default option toggling" data-action="left"> <button type="button" class="btn btn-default option toggling" data-action="left">
@@ -422,45 +415,6 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-sm-6">
<div class="textcontainer">
<div class="btn-group btn-group-justified" id="toolbox-verticalalign">
<div class="btn-group" role="group">
<button type="button" class="btn btn-default option toggling" data-action="top">
<span class="fa fa-toggle-down"></span>
</button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default option toggling" data-action="middle">
<span class="fa fa-sort"></span>
</button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default option toggling" data-action="bottom">
<span class="fa fa-toggle-up"></span>
</button>
</div>
</div>
</div>
</div>
</div>
<div class="row control-group textcontainer">
<div class="col-sm-6">
<div class="btn-group btn-group-justified">
<div class="btn-group textcontainer" role="group">
<button type="button" class="btn btn-default toggling" data-action="autoresize"
data-toggle="tooltip" title="{% trans "Automatically reduce font size to fit content" %}">
<span class="fa fa-text-height"></span>
</button>
</div>
<div class="btn-group textcontainer" role="group">
<button type="button" class="btn btn-default toggling" data-action="splitlongwords"
data-toggle="tooltip" title="{% trans "Allow long words to be split (preview is not accurate)" %}">
<span class="fa fa-scissors fa-rotate-90"></span>
</button>
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -469,13 +423,9 @@
{% trans "Add a new object" %} {% trans "Add a new object" %}
</div> </div>
<div class="panel-body add-buttons"> <div class="panel-body add-buttons">
<button class="btn btn-default btn-block" id="editor-add-textcontainer" disabled>
<span class="fa fa-font"></span>
{% trans "Text box" %}
</button>
<button class="btn btn-default btn-block" id="editor-add-text" disabled> <button class="btn btn-default btn-block" id="editor-add-text" disabled>
<span class="fa fa-font"></span> <span class="fa fa-font"></span>
{% trans "Text (deprecated)" %} {% trans "Text" %}
</button> </button>
<button class="btn btn-default btn-block" id="editor-add-qrcode" data-content="secret" disabled> <button class="btn btn-default btn-block" id="editor-add-qrcode" data-content="secret" disabled>
<span class="fa fa-qrcode"></span> <span class="fa fa-qrcode"></span>
@@ -506,6 +456,7 @@
</button> </button>
</div> </div>
</div> </div>
<form method="post" action="" id="preview-form" target="_blank"> <form method="post" action="" id="preview-form" target="_blank">
<div class="form-group submit-group"> <div class="form-group submit-group">
{% csrf_token %} {% csrf_token %}
@@ -521,13 +472,6 @@
</button> </button>
</div> </div>
</form> </form>
<p>&nbsp;</p>
<div class="alert alert-info" id="version-notice">
{% blocktrans trimmed with print_version="2.18" scan_version="1.22" %}
This layout uses new features. If you print from your device, make sure you use pretixPRINT version
{{ print_version }} (or newer) or pretixSCAN Desktop version {{ scan_version }} (or newer).
{% endblocktrans %}
</div>
</div> </div>
</div> </div>
<script type="text/plain" id="schema-url">{% static "schema/pdf-layout.schema.json" %}</script> <script type="text/plain" id="schema-url">{% static "schema/pdf-layout.schema.json" %}</script>
@@ -5,13 +5,7 @@
{% load urlreplace %} {% load urlreplace %}
{% block title %}{% trans "Waiting list" %}{% endblock %} {% block title %}{% trans "Waiting list" %}{% endblock %}
{% block content %} {% block content %}
<h1> <h1>{% trans "Waiting list" %}</h1>
{% trans "Waiting list" %}
<a href="{% url "control:event.settings" event=request.event.slug organizer=request.organizer.slug %}#waiting-list-open" class="btn btn-default">
<span class="fa fa-cog"></span>
{% trans "Settings" %}
</a>
</h1>
{% if not request.event.settings.waiting_list_enabled %} {% if not request.event.settings.waiting_list_enabled %}
<div class="alert alert-danger"> <div class="alert alert-danger">
{% trans "The waiting list is disabled, so if the event is sold out, people cannot add themselves to this list. If you want to enable it, go to the event settings." %} {% trans "The waiting list is disabled, so if the event is sold out, people cannot add themselves to this list. If you want to enable it, go to the event settings." %}
+4 -6
View File
@@ -62,7 +62,6 @@ from django.http import (
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse from django.urls import reverse
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.html import escape
from django.utils.http import url_has_allowed_host_and_scheme from django.utils.http import url_has_allowed_host_and_scheme
from django.utils.timezone import now from django.utils.timezone import now
from django.utils.translation import gettext, gettext_lazy as _, gettext_noop from django.utils.translation import gettext, gettext_lazy as _, gettext_noop
@@ -99,7 +98,6 @@ from ...base.i18n import language
from ...base.models.items import ( from ...base.models.items import (
Item, ItemCategory, ItemMetaProperty, Question, Quota, Item, ItemCategory, ItemMetaProperty, Question, Quota,
) )
from ...base.services.mail import prefix_subject
from ...base.settings import LazyI18nStringList from ...base.settings import LazyI18nStringList
from ...helpers.compat import CompatDeleteView from ...helpers.compat import CompatDeleteView
from ...helpers.format import format_map from ...helpers.format import format_map
@@ -728,7 +726,7 @@ class MailSettingsPreview(EventPermissionRequiredMixin, View):
else: else:
ctx[p.identifier] = '<span class="placeholder" title="{}">{}</span>'.format( ctx[p.identifier] = '<span class="placeholder" title="{}">{}</span>'.format(
_('This value will be replaced based on dynamic parameters.'), _('This value will be replaced based on dynamic parameters.'),
escape(s) s
) )
return ctx return ctx
@@ -748,9 +746,9 @@ class MailSettingsPreview(EventPermissionRequiredMixin, View):
with language(self.supported_locale[idx], self.request.event.settings.region): with language(self.supported_locale[idx], self.request.event.settings.region):
try: try:
if k.startswith('mail_subject_'): if k.startswith('mail_subject_'):
msgs[self.supported_locale[idx]] = prefix_subject(self.request.event, format_map( msgs[self.supported_locale[idx]] = format_map(
bleach.clean(v), self.placeholders(preview_item), raise_on_missing=True bleach.clean(v), self.placeholders(preview_item), raise_on_missing=True
), highlight=True) )
else: else:
msgs[self.supported_locale[idx]] = markdown_compile_email( msgs[self.supported_locale[idx]] = markdown_compile_email(
format_map(v, self.placeholders(preview_item), raise_on_missing=True) format_map(v, self.placeholders(preview_item), raise_on_missing=True)
@@ -778,7 +776,7 @@ class MailSettingsRendererPreview(MailSettingsPreview):
def placeholders(self, item): def placeholders(self, item):
ctx = {} ctx = {}
for p in get_available_placeholders(self.request.event, MailSettingsForm.base_context[item]).values(): for p in get_available_placeholders(self.request.event, MailSettingsForm.base_context[item]).values():
ctx[p.identifier] = escape(str(p.render_sample(self.request.event))) ctx[p.identifier] = str(p.render_sample(self.request.event))
return ctx return ctx
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
+5 -12
View File
@@ -62,9 +62,8 @@ from django.urls import reverse
from django.utils import formats from django.utils import formats
from django.utils.formats import date_format, get_format from django.utils.formats import date_format, get_format
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.html import conditional_escape, escape from django.utils.html import conditional_escape
from django.utils.http import url_has_allowed_host_and_scheme from django.utils.http import url_has_allowed_host_and_scheme
from django.utils.safestring import mark_safe
from django.utils.timezone import make_aware, now from django.utils.timezone import make_aware, now
from django.utils.translation import gettext, gettext_lazy as _, ngettext from django.utils.translation import gettext, gettext_lazy as _, ngettext
from django.views.generic import ( from django.views.generic import (
@@ -95,9 +94,7 @@ from pretix.base.services.invoices import (
invoice_qualified, regenerate_invoice, invoice_qualified, regenerate_invoice,
) )
from pretix.base.services.locking import LockTimeoutException from pretix.base.services.locking import LockTimeoutException
from pretix.base.services.mail import ( from pretix.base.services.mail import SendMailException, render_mail
SendMailException, prefix_subject, render_mail,
)
from pretix.base.services.orders import ( from pretix.base.services.orders import (
OrderChangeManager, OrderError, approve_order, cancel_order, deny_order, OrderChangeManager, OrderError, approve_order, cancel_order, deny_order,
extend_order, mark_order_expired, mark_order_refunded, extend_order, mark_order_expired, mark_order_refunded,
@@ -1903,7 +1900,7 @@ class OrderChange(OrderView):
positions = list(self.order.positions.select_related( positions = list(self.order.positions.select_related(
'item', 'item__tax_rule', 'used_membership', 'used_membership__membership_type', 'tax_rule', 'item', 'item__tax_rule', 'used_membership', 'used_membership__membership_type', 'tax_rule',
'seat', 'subevent', 'seat', 'subevent',
).prefetch_related('granted_memberships')) ))
for p in positions: for p in positions:
p.form = OrderPositionChangeForm(prefix='op-{}'.format(p.pk), instance=p, items=self.items, p.form = OrderPositionChangeForm(prefix='op-{}'.format(p.pk), instance=p, items=self.items,
initial={'seat': p.seat.seat_guid if p.seat else None}, initial={'seat': p.seat.seat_guid if p.seat else None},
@@ -2307,9 +2304,7 @@ class OrderSendMail(EventPermissionRequiredMixin, OrderViewMixin, FormView):
email_content = render_mail(email_template, email_context) email_content = render_mail(email_template, email_context)
if self.request.POST.get('action') == 'preview': if self.request.POST.get('action') == 'preview':
self.preview_output = { self.preview_output = {
'subject': mark_safe(_('Subject: {subject}').format( 'subject': _('Subject: {subject}').format(subject=email_subject),
subject=prefix_subject(order.event, escape(email_subject), highlight=True)
)),
'html': markdown_compile_email(email_content) 'html': markdown_compile_email(email_content)
} }
return self.get(self.request, *self.args, **self.kwargs) return self.get(self.request, *self.args, **self.kwargs)
@@ -2374,9 +2369,7 @@ class OrderPositionSendMail(OrderSendMail):
email_content = render_mail(email_template, email_context) email_content = render_mail(email_template, email_context)
if self.request.POST.get('action') == 'preview': if self.request.POST.get('action') == 'preview':
self.preview_output = { self.preview_output = {
'subject': mark_safe(_('Subject: {subject}').format( 'subject': _('Subject: {subject}').format(subject=email_subject),
subject=prefix_subject(position.order.event, escape(email_subject), highlight=True))
),
'html': markdown_compile_email(email_content) 'html': markdown_compile_email(email_content)
} }
return self.get(self.request, *self.args, **self.kwargs) return self.get(self.request, *self.args, **self.kwargs)
+10 -24
View File
@@ -90,7 +90,7 @@ from pretix.base.models.orders import CancellationRequest
from pretix.base.models.organizer import SalesChannel, TeamAPIToken from pretix.base.models.organizer import SalesChannel, TeamAPIToken
from pretix.base.payment import PaymentException from pretix.base.payment import PaymentException
from pretix.base.services.export import multiexport, scheduled_organizer_export from pretix.base.services.export import multiexport, scheduled_organizer_export
from pretix.base.services.mail import SendMailException, mail, prefix_subject from pretix.base.services.mail import SendMailException, mail
from pretix.base.signals import register_multievent_data_exporters from pretix.base.signals import register_multievent_data_exporters
from pretix.base.templatetags.rich_text import markdown_compile_email from pretix.base.templatetags.rich_text import markdown_compile_email
from pretix.base.views.tasks import AsyncAction from pretix.base.views.tasks import AsyncAction
@@ -351,11 +351,8 @@ class MailSettingsPreview(OrganizerPermissionRequiredMixin, View):
if idx in self.supported_locale: if idx in self.supported_locale:
with language(self.supported_locale[idx], self.request.organizer.settings.region): with language(self.supported_locale[idx], self.request.organizer.settings.region):
if k.startswith('mail_subject_'): if k.startswith('mail_subject_'):
msgs[self.supported_locale[idx]] = prefix_subject( msgs[self.supported_locale[idx]] = format_map(bleach.clean(v),
self.request.organizer, self.placeholders(preview_item))
format_map(bleach.clean(v), self.placeholders(preview_item)),
highlight=True,
)
else: else:
msgs[self.supported_locale[idx]] = markdown_compile_email( msgs[self.supported_locale[idx]] = markdown_compile_email(
format_map(v, self.placeholders(preview_item)) format_map(v, self.placeholders(preview_item))
@@ -686,24 +683,14 @@ class TeamDeleteView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin,
try: try:
self.object.log_action('pretix.team.deleted', user=self.request.user) self.object.log_action('pretix.team.deleted', user=self.request.user)
self.object.delete() self.object.delete()
except ProtectedError as e: except ProtectedError:
is_logs = any(isinstance(e, LogEntry) for e in e.protected_objects) messages.error(
if is_logs: self.request,
messages.error( _(
self.request, 'The team could not be deleted as some constraints (e.g. data created by '
_( 'plug-ins) do not allow it.'
"The team could not be deleted because the team or one of its API tokens is part of "
"historical audit logs."
)
)
else:
messages.error(
self.request,
_(
'The team could not be deleted as some constraints (e.g. data created by '
'plug-ins) do not allow it.'
)
) )
)
return redirect(success_url) return redirect(success_url)
messages.success(request, _('The selected team has been deleted.')) messages.success(request, _('The selected team has been deleted.'))
@@ -733,7 +720,6 @@ class TeamMemberView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin,
ctx = super().get_context_data(**kwargs) ctx = super().get_context_data(**kwargs)
ctx['add_form'] = self.add_form ctx['add_form'] = self.add_form
ctx['add_token_form'] = self.add_token_form ctx['add_token_form'] = self.add_token_form
ctx['tokens'] = self.object.tokens.order_by("-active", "name", "pk")
return ctx return ctx
def _send_invite(self, instance): def _send_invite(self, instance):
+3 -8
View File
@@ -50,7 +50,7 @@ from django.http import (
from django.shortcuts import redirect, render from django.shortcuts import redirect, render
from django.urls import resolve, reverse from django.urls import resolve, reverse
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.html import escape, format_html from django.utils.html import format_html
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.timezone import now from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@@ -64,7 +64,6 @@ from pretix.base.models import (
CartPosition, LogEntry, Voucher, WaitingListEntry, CartPosition, LogEntry, Voucher, WaitingListEntry,
) )
from pretix.base.models.vouchers import generate_codes from pretix.base.models.vouchers import generate_codes
from pretix.base.services.mail import prefix_subject
from pretix.base.services.vouchers import vouchers_send from pretix.base.services.vouchers import vouchers_send
from pretix.base.templatetags.rich_text import markdown_compile_email from pretix.base.templatetags.rich_text import markdown_compile_email
from pretix.base.views.tasks import AsyncFormView from pretix.base.views.tasks import AsyncFormView
@@ -563,7 +562,7 @@ class VoucherBulkMailPreview(EventPermissionRequiredMixin, View):
else: else:
ctx[p.identifier] = '<span class="placeholder" title="{}">{}</span>'.format( ctx[p.identifier] = '<span class="placeholder" title="{}">{}</span>'.format(
_('This value will be replaced based on dynamic parameters.'), _('This value will be replaced based on dynamic parameters.'),
escape(s) s
) )
return self.SafeDict(ctx) return self.SafeDict(ctx)
@@ -573,11 +572,7 @@ class VoucherBulkMailPreview(EventPermissionRequiredMixin, View):
return HttpResponseBadRequest(_('invalid item')) return HttpResponseBadRequest(_('invalid item'))
msgs = {} msgs = {}
if "subject" in preview_item: if "subject" in preview_item:
msgs["all"] = prefix_subject( msgs["all"] = format_map(bleach.clean(request.POST.get(preview_item, "")), self.placeholders(preview_item))
self.request.event,
format_map(bleach.clean(request.POST.get(preview_item, "")), self.placeholders(preview_item)),
highlight=True
)
else: else:
msgs["all"] = markdown_compile_email( msgs["all"] = markdown_compile_email(
format_map(request.POST.get(preview_item), self.placeholders(preview_item)) format_map(request.POST.get(preview_item), self.placeholders(preview_item))
+2 -2
View File
@@ -53,14 +53,14 @@ def daterange(df, dt, as_html=False):
if lng.startswith("de"): if lng.startswith("de"):
if df.year == dt.year and df.month == dt.month and df.day == dt.day: if df.year == dt.year and df.month == dt.month and df.day == dt.day:
return format_html(base_format, _date(df, "D, j. F Y")) return format_html(base_format, _date(df, "j. F Y"))
elif df.year == dt.year and df.month == dt.month: elif df.year == dt.year and df.month == dt.month:
return format_html(base_format, _date(df, "j."), "", _date(dt, "j. F Y")) return format_html(base_format, _date(df, "j."), "", _date(dt, "j. F Y"))
elif df.year == dt.year: elif df.year == dt.year:
return format_html(base_format, _date(df, "j. F"), " ", _date(dt, "j. F Y")) return format_html(base_format, _date(df, "j. F"), " ", _date(dt, "j. F Y"))
elif lng.startswith("en"): elif lng.startswith("en"):
if df.year == dt.year and df.month == dt.month and df.day == dt.day: if df.year == dt.year and df.month == dt.month and df.day == dt.day:
return format_html(base_format, _date(df, "D, N jS, Y")) return format_html(base_format, _date(df, "N jS, Y"))
elif df.year == dt.year and df.month == dt.month: elif df.year == dt.year and df.month == dt.month:
return format_html(base_format, _date(df, "N jS"), " ", _date(dt, "jS, Y")) return format_html(base_format, _date(df, "N jS"), " ", _date(dt, "jS, Y"))
elif df.year == dt.year: elif df.year == dt.year:
File diff suppressed because it is too large Load Diff
+33 -37
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-08-27 13:34+0000\n" "POT-Creation-Date: 2024-06-24 08:20+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -543,56 +543,52 @@ msgctxt "entry_status"
msgid "absent" msgid "absent"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:171 #: pretix/static/pretixcontrol/js/ui/editor.js:72
msgid "Check-in QR" msgid "Check-in QR"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:543 #: pretix/static/pretixcontrol/js/ui/editor.js:387
msgid "The PDF background file could not be loaded for the following reason:" msgid "The PDF background file could not be loaded for the following reason:"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:894 #: pretix/static/pretixcontrol/js/ui/editor.js:656
msgid "Group of objects" msgid "Group of objects"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:899 #: pretix/static/pretixcontrol/js/ui/editor.js:661
msgid "Text object (deprecated)" msgid "Text object"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:901 #: pretix/static/pretixcontrol/js/ui/editor.js:663
msgid "Text box"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:903
msgid "Barcode area" msgid "Barcode area"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:905 #: pretix/static/pretixcontrol/js/ui/editor.js:665
msgid "Image area" msgid "Image area"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:907 #: pretix/static/pretixcontrol/js/ui/editor.js:667
msgid "Powered by pretix" msgid "Powered by pretix"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:909 #: pretix/static/pretixcontrol/js/ui/editor.js:669
msgid "Object" msgid "Object"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:913 #: pretix/static/pretixcontrol/js/ui/editor.js:673
msgid "Ticket design" msgid "Ticket design"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:1250 #: pretix/static/pretixcontrol/js/ui/editor.js:972
msgid "Saving failed." msgid "Saving failed."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:1319 #: pretix/static/pretixcontrol/js/ui/editor.js:1041
#: pretix/static/pretixcontrol/js/ui/editor.js:1370 #: pretix/static/pretixcontrol/js/ui/editor.js:1091
msgid "Error while uploading your PDF file, please try again." msgid "Error while uploading your PDF file, please try again."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:1353 #: pretix/static/pretixcontrol/js/ui/editor.js:1074
msgid "Do you really want to leave the editor without saving your changes?" msgid "Do you really want to leave the editor without saving your changes?"
msgstr "" msgstr ""
@@ -608,54 +604,54 @@ msgstr ""
msgid "Unknown error." msgid "Unknown error."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:318 #: pretix/static/pretixcontrol/js/ui/main.js:313
msgid "Your color has great contrast and is very easy to read!" msgid "Your color has great contrast and is very easy to read!"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:322 #: pretix/static/pretixcontrol/js/ui/main.js:317
msgid "Your color has decent contrast and is probably good-enough to read!" msgid "Your color has decent contrast and is probably good-enough to read!"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:326 #: pretix/static/pretixcontrol/js/ui/main.js:321
msgid "" msgid ""
"Your color has bad contrast for text on white background, please choose a " "Your color has bad contrast for text on white background, please choose a "
"darker shade." "darker shade."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:491 #: pretix/static/pretixcontrol/js/ui/main.js:475
#: pretix/static/pretixcontrol/js/ui/main.js:511 #: pretix/static/pretixcontrol/js/ui/main.js:495
msgid "Search query" msgid "Search query"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:509 #: pretix/static/pretixcontrol/js/ui/main.js:493
msgid "All" msgid "All"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:510 #: pretix/static/pretixcontrol/js/ui/main.js:494
msgid "None" msgid "None"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:514 #: pretix/static/pretixcontrol/js/ui/main.js:498
msgid "Selected only" msgid "Selected only"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:847 #: pretix/static/pretixcontrol/js/ui/main.js:828
msgid "Enter page number between 1 and %(max)s." msgid "Enter page number between 1 and %(max)s."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:850 #: pretix/static/pretixcontrol/js/ui/main.js:831
msgid "Invalid page number." msgid "Invalid page number."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1008 #: pretix/static/pretixcontrol/js/ui/main.js:989
msgid "Use a different name internally" msgid "Use a different name internally"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1048 #: pretix/static/pretixcontrol/js/ui/main.js:1029
msgid "Click to close" msgid "Click to close"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1123 #: pretix/static/pretixcontrol/js/ui/main.js:1104
msgid "You have unsaved changes!" msgid "You have unsaved changes!"
msgstr "" msgstr ""
@@ -705,20 +701,20 @@ msgstr ""
msgid "Please enter the amount the organizer can keep." msgid "Please enter the amount the organizer can keep."
msgstr "" msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:449 #: pretix/static/pretixpresale/js/ui/main.js:444
msgid "Please enter a quantity for one of the ticket types." msgid "Please enter a quantity for one of the ticket types."
msgstr "" msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:485 #: pretix/static/pretixpresale/js/ui/main.js:480
msgid "required" msgid "required"
msgstr "" msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:588 #: pretix/static/pretixpresale/js/ui/main.js:583
#: pretix/static/pretixpresale/js/ui/main.js:607 #: pretix/static/pretixpresale/js/ui/main.js:602
msgid "Time zone:" msgid "Time zone:"
msgstr "" msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:598 #: pretix/static/pretixpresale/js/ui/main.js:593
msgid "Your local time:" msgid "Your local time:"
msgstr "" msgstr ""
File diff suppressed because it is too large Load Diff
+33 -41
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-08-27 13:34+0000\n" "POT-Creation-Date: 2024-06-24 08:20+0000\n"
"PO-Revision-Date: 2021-09-15 11:22+0000\n" "PO-Revision-Date: 2021-09-15 11:22+0000\n"
"Last-Translator: Mohamed Tawfiq <mtawfiq@wafyapp.com>\n" "Last-Translator: Mohamed Tawfiq <mtawfiq@wafyapp.com>\n"
"Language-Team: Arabic <https://translate.pretix.eu/projects/pretix/pretix-js/" "Language-Team: Arabic <https://translate.pretix.eu/projects/pretix/pretix-js/"
@@ -573,60 +573,52 @@ msgctxt "entry_status"
msgid "absent" msgid "absent"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:171 #: pretix/static/pretixcontrol/js/ui/editor.js:72
msgid "Check-in QR" msgid "Check-in QR"
msgstr "QR الدخول" msgstr "QR الدخول"
#: pretix/static/pretixcontrol/js/ui/editor.js:543 #: pretix/static/pretixcontrol/js/ui/editor.js:387
msgid "The PDF background file could not be loaded for the following reason:" msgid "The PDF background file could not be loaded for the following reason:"
msgstr "لا يمكن تحميل ملف PDF الخلفية للأسباب التالية:" msgstr "لا يمكن تحميل ملف PDF الخلفية للأسباب التالية:"
#: pretix/static/pretixcontrol/js/ui/editor.js:894 #: pretix/static/pretixcontrol/js/ui/editor.js:656
msgid "Group of objects" msgid "Group of objects"
msgstr "مجموعة من العناصر" msgstr "مجموعة من العناصر"
#: pretix/static/pretixcontrol/js/ui/editor.js:899 #: pretix/static/pretixcontrol/js/ui/editor.js:661
#, fuzzy msgid "Text object"
#| msgid "Text object"
msgid "Text object (deprecated)"
msgstr "عنصر نص" msgstr "عنصر نص"
#: pretix/static/pretixcontrol/js/ui/editor.js:901 #: pretix/static/pretixcontrol/js/ui/editor.js:663
#, fuzzy
#| msgid "Text object"
msgid "Text box"
msgstr "عنصر نص"
#: pretix/static/pretixcontrol/js/ui/editor.js:903
msgid "Barcode area" msgid "Barcode area"
msgstr "منطقة باركود" msgstr "منطقة باركود"
#: pretix/static/pretixcontrol/js/ui/editor.js:905 #: pretix/static/pretixcontrol/js/ui/editor.js:665
msgid "Image area" msgid "Image area"
msgstr "منطقة صورة" msgstr "منطقة صورة"
#: pretix/static/pretixcontrol/js/ui/editor.js:907 #: pretix/static/pretixcontrol/js/ui/editor.js:667
msgid "Powered by pretix" msgid "Powered by pretix"
msgstr "مدعوم من pretix" msgstr "مدعوم من pretix"
#: pretix/static/pretixcontrol/js/ui/editor.js:909 #: pretix/static/pretixcontrol/js/ui/editor.js:669
msgid "Object" msgid "Object"
msgstr "عنصر" msgstr "عنصر"
#: pretix/static/pretixcontrol/js/ui/editor.js:913 #: pretix/static/pretixcontrol/js/ui/editor.js:673
msgid "Ticket design" msgid "Ticket design"
msgstr "تصميم التذكرة" msgstr "تصميم التذكرة"
#: pretix/static/pretixcontrol/js/ui/editor.js:1250 #: pretix/static/pretixcontrol/js/ui/editor.js:972
msgid "Saving failed." msgid "Saving failed."
msgstr "فشلت عملية الحفظ." msgstr "فشلت عملية الحفظ."
#: pretix/static/pretixcontrol/js/ui/editor.js:1319 #: pretix/static/pretixcontrol/js/ui/editor.js:1041
#: pretix/static/pretixcontrol/js/ui/editor.js:1370 #: pretix/static/pretixcontrol/js/ui/editor.js:1091
msgid "Error while uploading your PDF file, please try again." msgid "Error while uploading your PDF file, please try again."
msgstr "حصل خطأ أثناء رفع ملف PDF الخاص بك، يرجى المحاولة مرة أخرى." msgstr "حصل خطأ أثناء رفع ملف PDF الخاص بك، يرجى المحاولة مرة أخرى."
#: pretix/static/pretixcontrol/js/ui/editor.js:1353 #: pretix/static/pretixcontrol/js/ui/editor.js:1074
msgid "Do you really want to leave the editor without saving your changes?" msgid "Do you really want to leave the editor without saving your changes?"
msgstr "هل تريد أن تغادر المحرر دون حفظ التعديلات؟" msgstr "هل تريد أن تغادر المحرر دون حفظ التعديلات؟"
@@ -642,54 +634,54 @@ msgstr "توليد الرسائل …"
msgid "Unknown error." msgid "Unknown error."
msgstr "خطأ غير معروف." msgstr "خطأ غير معروف."
#: pretix/static/pretixcontrol/js/ui/main.js:318 #: pretix/static/pretixcontrol/js/ui/main.js:313
msgid "Your color has great contrast and is very easy to read!" msgid "Your color has great contrast and is very easy to read!"
msgstr "اللون يتمتع بتباين كبير وتسهل قراءته!" msgstr "اللون يتمتع بتباين كبير وتسهل قراءته!"
#: pretix/static/pretixcontrol/js/ui/main.js:322 #: pretix/static/pretixcontrol/js/ui/main.js:317
msgid "Your color has decent contrast and is probably good-enough to read!" msgid "Your color has decent contrast and is probably good-enough to read!"
msgstr "اللون يحظى بتباين معقول ويمكن أن يكون مناسب للقراءة!" msgstr "اللون يحظى بتباين معقول ويمكن أن يكون مناسب للقراءة!"
#: pretix/static/pretixcontrol/js/ui/main.js:326 #: pretix/static/pretixcontrol/js/ui/main.js:321
msgid "" msgid ""
"Your color has bad contrast for text on white background, please choose a " "Your color has bad contrast for text on white background, please choose a "
"darker shade." "darker shade."
msgstr "تباين اللون سيئ للخلفية البيضاء، الرجاء اختيار لون غامق." msgstr "تباين اللون سيئ للخلفية البيضاء، الرجاء اختيار لون غامق."
#: pretix/static/pretixcontrol/js/ui/main.js:491 #: pretix/static/pretixcontrol/js/ui/main.js:475
#: pretix/static/pretixcontrol/js/ui/main.js:511 #: pretix/static/pretixcontrol/js/ui/main.js:495
msgid "Search query" msgid "Search query"
msgstr "البحث في الاستفسارات" msgstr "البحث في الاستفسارات"
#: pretix/static/pretixcontrol/js/ui/main.js:509 #: pretix/static/pretixcontrol/js/ui/main.js:493
msgid "All" msgid "All"
msgstr "الكل" msgstr "الكل"
#: pretix/static/pretixcontrol/js/ui/main.js:510 #: pretix/static/pretixcontrol/js/ui/main.js:494
msgid "None" msgid "None"
msgstr "لا شيء" msgstr "لا شيء"
#: pretix/static/pretixcontrol/js/ui/main.js:514 #: pretix/static/pretixcontrol/js/ui/main.js:498
msgid "Selected only" msgid "Selected only"
msgstr "المختارة فقط" msgstr "المختارة فقط"
#: pretix/static/pretixcontrol/js/ui/main.js:847 #: pretix/static/pretixcontrol/js/ui/main.js:828
msgid "Enter page number between 1 and %(max)s." msgid "Enter page number between 1 and %(max)s."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:850 #: pretix/static/pretixcontrol/js/ui/main.js:831
msgid "Invalid page number." msgid "Invalid page number."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1008 #: pretix/static/pretixcontrol/js/ui/main.js:989
msgid "Use a different name internally" msgid "Use a different name internally"
msgstr "قم باستخدم اسم مختلف داخليا" msgstr "قم باستخدم اسم مختلف داخليا"
#: pretix/static/pretixcontrol/js/ui/main.js:1048 #: pretix/static/pretixcontrol/js/ui/main.js:1029
msgid "Click to close" msgid "Click to close"
msgstr "اضغط لاغلاق الصفحة" msgstr "اضغط لاغلاق الصفحة"
#: pretix/static/pretixcontrol/js/ui/main.js:1123 #: pretix/static/pretixcontrol/js/ui/main.js:1104
msgid "You have unsaved changes!" msgid "You have unsaved changes!"
msgstr "لم تقم بحفظ التعديلات!" msgstr "لم تقم بحفظ التعديلات!"
@@ -753,20 +745,20 @@ msgstr "ستسترد %(currency)%(amount)"
msgid "Please enter the amount the organizer can keep." msgid "Please enter the amount the organizer can keep."
msgstr "الرجاء إدخال المبلغ الذي يمكن للمنظم الاحتفاظ به." msgstr "الرجاء إدخال المبلغ الذي يمكن للمنظم الاحتفاظ به."
#: pretix/static/pretixpresale/js/ui/main.js:449 #: pretix/static/pretixpresale/js/ui/main.js:444
msgid "Please enter a quantity for one of the ticket types." msgid "Please enter a quantity for one of the ticket types."
msgstr "الرجاء إدخال عدد لأحد أنواع التذاكر." msgstr "الرجاء إدخال عدد لأحد أنواع التذاكر."
#: pretix/static/pretixpresale/js/ui/main.js:485 #: pretix/static/pretixpresale/js/ui/main.js:480
msgid "required" msgid "required"
msgstr "مطلوب" msgstr "مطلوب"
#: pretix/static/pretixpresale/js/ui/main.js:588 #: pretix/static/pretixpresale/js/ui/main.js:583
#: pretix/static/pretixpresale/js/ui/main.js:607 #: pretix/static/pretixpresale/js/ui/main.js:602
msgid "Time zone:" msgid "Time zone:"
msgstr "المنطقة الزمنية:" msgstr "المنطقة الزمنية:"
#: pretix/static/pretixpresale/js/ui/main.js:598 #: pretix/static/pretixpresale/js/ui/main.js:593
msgid "Your local time:" msgid "Your local time:"
msgstr "التوقيت المحلي:" msgstr "التوقيت المحلي:"
File diff suppressed because it is too large Load Diff
+33 -37
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-08-27 13:34+0000\n" "POT-Creation-Date: 2024-06-24 08:20+0000\n"
"PO-Revision-Date: 2020-12-19 07:00+0000\n" "PO-Revision-Date: 2020-12-19 07:00+0000\n"
"Last-Translator: albert <albert.serra.monner@gmail.com>\n" "Last-Translator: albert <albert.serra.monner@gmail.com>\n"
"Language-Team: Catalan <https://translate.pretix.eu/projects/pretix/pretix-" "Language-Team: Catalan <https://translate.pretix.eu/projects/pretix/pretix-"
@@ -544,56 +544,52 @@ msgctxt "entry_status"
msgid "absent" msgid "absent"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:171 #: pretix/static/pretixcontrol/js/ui/editor.js:72
msgid "Check-in QR" msgid "Check-in QR"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:543 #: pretix/static/pretixcontrol/js/ui/editor.js:387
msgid "The PDF background file could not be loaded for the following reason:" msgid "The PDF background file could not be loaded for the following reason:"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:894 #: pretix/static/pretixcontrol/js/ui/editor.js:656
msgid "Group of objects" msgid "Group of objects"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:899 #: pretix/static/pretixcontrol/js/ui/editor.js:661
msgid "Text object (deprecated)" msgid "Text object"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:901 #: pretix/static/pretixcontrol/js/ui/editor.js:663
msgid "Text box"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:903
msgid "Barcode area" msgid "Barcode area"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:905 #: pretix/static/pretixcontrol/js/ui/editor.js:665
msgid "Image area" msgid "Image area"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:907 #: pretix/static/pretixcontrol/js/ui/editor.js:667
msgid "Powered by pretix" msgid "Powered by pretix"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:909 #: pretix/static/pretixcontrol/js/ui/editor.js:669
msgid "Object" msgid "Object"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:913 #: pretix/static/pretixcontrol/js/ui/editor.js:673
msgid "Ticket design" msgid "Ticket design"
msgstr "Disseny del tiquet" msgstr "Disseny del tiquet"
#: pretix/static/pretixcontrol/js/ui/editor.js:1250 #: pretix/static/pretixcontrol/js/ui/editor.js:972
msgid "Saving failed." msgid "Saving failed."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:1319 #: pretix/static/pretixcontrol/js/ui/editor.js:1041
#: pretix/static/pretixcontrol/js/ui/editor.js:1370 #: pretix/static/pretixcontrol/js/ui/editor.js:1091
msgid "Error while uploading your PDF file, please try again." msgid "Error while uploading your PDF file, please try again."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:1353 #: pretix/static/pretixcontrol/js/ui/editor.js:1074
msgid "Do you really want to leave the editor without saving your changes?" msgid "Do you really want to leave the editor without saving your changes?"
msgstr "" msgstr ""
@@ -609,54 +605,54 @@ msgstr ""
msgid "Unknown error." msgid "Unknown error."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:318 #: pretix/static/pretixcontrol/js/ui/main.js:313
msgid "Your color has great contrast and is very easy to read!" msgid "Your color has great contrast and is very easy to read!"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:322 #: pretix/static/pretixcontrol/js/ui/main.js:317
msgid "Your color has decent contrast and is probably good-enough to read!" msgid "Your color has decent contrast and is probably good-enough to read!"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:326 #: pretix/static/pretixcontrol/js/ui/main.js:321
msgid "" msgid ""
"Your color has bad contrast for text on white background, please choose a " "Your color has bad contrast for text on white background, please choose a "
"darker shade." "darker shade."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:491 #: pretix/static/pretixcontrol/js/ui/main.js:475
#: pretix/static/pretixcontrol/js/ui/main.js:511 #: pretix/static/pretixcontrol/js/ui/main.js:495
msgid "Search query" msgid "Search query"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:509 #: pretix/static/pretixcontrol/js/ui/main.js:493
msgid "All" msgid "All"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:510 #: pretix/static/pretixcontrol/js/ui/main.js:494
msgid "None" msgid "None"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:514 #: pretix/static/pretixcontrol/js/ui/main.js:498
msgid "Selected only" msgid "Selected only"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:847 #: pretix/static/pretixcontrol/js/ui/main.js:828
msgid "Enter page number between 1 and %(max)s." msgid "Enter page number between 1 and %(max)s."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:850 #: pretix/static/pretixcontrol/js/ui/main.js:831
msgid "Invalid page number." msgid "Invalid page number."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1008 #: pretix/static/pretixcontrol/js/ui/main.js:989
msgid "Use a different name internally" msgid "Use a different name internally"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1048 #: pretix/static/pretixcontrol/js/ui/main.js:1029
msgid "Click to close" msgid "Click to close"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1123 #: pretix/static/pretixcontrol/js/ui/main.js:1104
msgid "You have unsaved changes!" msgid "You have unsaved changes!"
msgstr "" msgstr ""
@@ -710,22 +706,22 @@ msgstr ""
msgid "Please enter the amount the organizer can keep." msgid "Please enter the amount the organizer can keep."
msgstr "" msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:449 #: pretix/static/pretixpresale/js/ui/main.js:444
msgid "Please enter a quantity for one of the ticket types." msgid "Please enter a quantity for one of the ticket types."
msgstr "" msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:485 #: pretix/static/pretixpresale/js/ui/main.js:480
#, fuzzy #, fuzzy
#| msgid "Cart expired" #| msgid "Cart expired"
msgid "required" msgid "required"
msgstr "Cistella expirada" msgstr "Cistella expirada"
#: pretix/static/pretixpresale/js/ui/main.js:588 #: pretix/static/pretixpresale/js/ui/main.js:583
#: pretix/static/pretixpresale/js/ui/main.js:607 #: pretix/static/pretixpresale/js/ui/main.js:602
msgid "Time zone:" msgid "Time zone:"
msgstr "" msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:598 #: pretix/static/pretixpresale/js/ui/main.js:593
msgid "Your local time:" msgid "Your local time:"
msgstr "" msgstr ""
File diff suppressed because it is too large Load Diff
+33 -41
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-08-27 13:34+0000\n" "POT-Creation-Date: 2024-06-24 08:20+0000\n"
"PO-Revision-Date: 2023-09-15 06:00+0000\n" "PO-Revision-Date: 2023-09-15 06:00+0000\n"
"Last-Translator: Michael <michael.happl@gmx.at>\n" "Last-Translator: Michael <michael.happl@gmx.at>\n"
"Language-Team: Czech <https://translate.pretix.eu/projects/pretix/pretix-js/" "Language-Team: Czech <https://translate.pretix.eu/projects/pretix/pretix-js/"
@@ -564,60 +564,52 @@ msgctxt "entry_status"
msgid "absent" msgid "absent"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:171 #: pretix/static/pretixcontrol/js/ui/editor.js:72
msgid "Check-in QR" msgid "Check-in QR"
msgstr "Check-in QR kód" msgstr "Check-in QR kód"
#: pretix/static/pretixcontrol/js/ui/editor.js:543 #: pretix/static/pretixcontrol/js/ui/editor.js:387
msgid "The PDF background file could not be loaded for the following reason:" msgid "The PDF background file could not be loaded for the following reason:"
msgstr "Pozadí PDF nemohl být načten:" msgstr "Pozadí PDF nemohl být načten:"
#: pretix/static/pretixcontrol/js/ui/editor.js:894 #: pretix/static/pretixcontrol/js/ui/editor.js:656
msgid "Group of objects" msgid "Group of objects"
msgstr "Skupina objektů" msgstr "Skupina objektů"
#: pretix/static/pretixcontrol/js/ui/editor.js:899 #: pretix/static/pretixcontrol/js/ui/editor.js:661
#, fuzzy msgid "Text object"
#| msgid "Text object"
msgid "Text object (deprecated)"
msgstr "Textový objekt" msgstr "Textový objekt"
#: pretix/static/pretixcontrol/js/ui/editor.js:901 #: pretix/static/pretixcontrol/js/ui/editor.js:663
#, fuzzy
#| msgid "Text object"
msgid "Text box"
msgstr "Textový objekt"
#: pretix/static/pretixcontrol/js/ui/editor.js:903
msgid "Barcode area" msgid "Barcode area"
msgstr "Oblast s QR kódem" msgstr "Oblast s QR kódem"
#: pretix/static/pretixcontrol/js/ui/editor.js:905 #: pretix/static/pretixcontrol/js/ui/editor.js:665
msgid "Image area" msgid "Image area"
msgstr "Oblast obrazu" msgstr "Oblast obrazu"
#: pretix/static/pretixcontrol/js/ui/editor.js:907 #: pretix/static/pretixcontrol/js/ui/editor.js:667
msgid "Powered by pretix" msgid "Powered by pretix"
msgstr "Poháněno společností pretix" msgstr "Poháněno společností pretix"
#: pretix/static/pretixcontrol/js/ui/editor.js:909 #: pretix/static/pretixcontrol/js/ui/editor.js:669
msgid "Object" msgid "Object"
msgstr "Objekt" msgstr "Objekt"
#: pretix/static/pretixcontrol/js/ui/editor.js:913 #: pretix/static/pretixcontrol/js/ui/editor.js:673
msgid "Ticket design" msgid "Ticket design"
msgstr "Design vstupenky" msgstr "Design vstupenky"
#: pretix/static/pretixcontrol/js/ui/editor.js:1250 #: pretix/static/pretixcontrol/js/ui/editor.js:972
msgid "Saving failed." msgid "Saving failed."
msgstr "Uložení se nepodařilo." msgstr "Uložení se nepodařilo."
#: pretix/static/pretixcontrol/js/ui/editor.js:1319 #: pretix/static/pretixcontrol/js/ui/editor.js:1041
#: pretix/static/pretixcontrol/js/ui/editor.js:1370 #: pretix/static/pretixcontrol/js/ui/editor.js:1091
msgid "Error while uploading your PDF file, please try again." msgid "Error while uploading your PDF file, please try again."
msgstr "Při nahrávání souboru PDF došlo k problému, zkuste to prosím znovu." msgstr "Při nahrávání souboru PDF došlo k problému, zkuste to prosím znovu."
#: pretix/static/pretixcontrol/js/ui/editor.js:1353 #: pretix/static/pretixcontrol/js/ui/editor.js:1074
msgid "Do you really want to leave the editor without saving your changes?" msgid "Do you really want to leave the editor without saving your changes?"
msgstr "Opravdu chcete opustit editor bez uložení změn?" msgstr "Opravdu chcete opustit editor bez uložení změn?"
@@ -633,16 +625,16 @@ msgstr "Vytváření zpráv…"
msgid "Unknown error." msgid "Unknown error."
msgstr "Neznámá chyba." msgstr "Neznámá chyba."
#: pretix/static/pretixcontrol/js/ui/main.js:318 #: pretix/static/pretixcontrol/js/ui/main.js:313
msgid "Your color has great contrast and is very easy to read!" msgid "Your color has great contrast and is very easy to read!"
msgstr "Tato barva má velmi dobrý kontrast a je velmi dobře čitelná!" msgstr "Tato barva má velmi dobrý kontrast a je velmi dobře čitelná!"
#: pretix/static/pretixcontrol/js/ui/main.js:322 #: pretix/static/pretixcontrol/js/ui/main.js:317
msgid "Your color has decent contrast and is probably good-enough to read!" msgid "Your color has decent contrast and is probably good-enough to read!"
msgstr "" msgstr ""
"Tato barva má slušný kontrast a pravděpodobně je dostatečně dobře čitelná!" "Tato barva má slušný kontrast a pravděpodobně je dostatečně dobře čitelná!"
#: pretix/static/pretixcontrol/js/ui/main.js:326 #: pretix/static/pretixcontrol/js/ui/main.js:321
msgid "" msgid ""
"Your color has bad contrast for text on white background, please choose a " "Your color has bad contrast for text on white background, please choose a "
"darker shade." "darker shade."
@@ -650,40 +642,40 @@ msgstr ""
"Tato barva je pro text na bílém pozadí špatně kontrastní, zvolte prosím " "Tato barva je pro text na bílém pozadí špatně kontrastní, zvolte prosím "
"tmavší odstín." "tmavší odstín."
#: pretix/static/pretixcontrol/js/ui/main.js:491 #: pretix/static/pretixcontrol/js/ui/main.js:475
#: pretix/static/pretixcontrol/js/ui/main.js:511 #: pretix/static/pretixcontrol/js/ui/main.js:495
msgid "Search query" msgid "Search query"
msgstr "Hledaný výraz" msgstr "Hledaný výraz"
#: pretix/static/pretixcontrol/js/ui/main.js:509 #: pretix/static/pretixcontrol/js/ui/main.js:493
msgid "All" msgid "All"
msgstr "Všechny" msgstr "Všechny"
#: pretix/static/pretixcontrol/js/ui/main.js:510 #: pretix/static/pretixcontrol/js/ui/main.js:494
msgid "None" msgid "None"
msgstr "Žádný" msgstr "Žádný"
#: pretix/static/pretixcontrol/js/ui/main.js:514 #: pretix/static/pretixcontrol/js/ui/main.js:498
msgid "Selected only" msgid "Selected only"
msgstr "Pouze vybrané" msgstr "Pouze vybrané"
#: pretix/static/pretixcontrol/js/ui/main.js:847 #: pretix/static/pretixcontrol/js/ui/main.js:828
msgid "Enter page number between 1 and %(max)s." msgid "Enter page number between 1 and %(max)s."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:850 #: pretix/static/pretixcontrol/js/ui/main.js:831
msgid "Invalid page number." msgid "Invalid page number."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1008 #: pretix/static/pretixcontrol/js/ui/main.js:989
msgid "Use a different name internally" msgid "Use a different name internally"
msgstr "Interně používat jiný název" msgstr "Interně používat jiný název"
#: pretix/static/pretixcontrol/js/ui/main.js:1048 #: pretix/static/pretixcontrol/js/ui/main.js:1029
msgid "Click to close" msgid "Click to close"
msgstr "Kliknutím zavřete" msgstr "Kliknutím zavřete"
#: pretix/static/pretixcontrol/js/ui/main.js:1123 #: pretix/static/pretixcontrol/js/ui/main.js:1104
msgid "You have unsaved changes!" msgid "You have unsaved changes!"
msgstr "Máte neuložené změny!" msgstr "Máte neuložené změny!"
@@ -740,20 +732,20 @@ msgstr "Dostanete %(currency)s %(amount)s zpět"
msgid "Please enter the amount the organizer can keep." msgid "Please enter the amount the organizer can keep."
msgstr "Zadejte částku, kterou si organizátor může ponechat." msgstr "Zadejte částku, kterou si organizátor může ponechat."
#: pretix/static/pretixpresale/js/ui/main.js:449 #: pretix/static/pretixpresale/js/ui/main.js:444
msgid "Please enter a quantity for one of the ticket types." msgid "Please enter a quantity for one of the ticket types."
msgstr "Zadejte prosím množství pro jeden z typů vstupenek." msgstr "Zadejte prosím množství pro jeden z typů vstupenek."
#: pretix/static/pretixpresale/js/ui/main.js:485 #: pretix/static/pretixpresale/js/ui/main.js:480
msgid "required" msgid "required"
msgstr "povinný" msgstr "povinný"
#: pretix/static/pretixpresale/js/ui/main.js:588 #: pretix/static/pretixpresale/js/ui/main.js:583
#: pretix/static/pretixpresale/js/ui/main.js:607 #: pretix/static/pretixpresale/js/ui/main.js:602
msgid "Time zone:" msgid "Time zone:"
msgstr "Časové pásmo:" msgstr "Časové pásmo:"
#: pretix/static/pretixpresale/js/ui/main.js:598 #: pretix/static/pretixpresale/js/ui/main.js:593
msgid "Your local time:" msgid "Your local time:"
msgstr "Místní čas:" msgstr "Místní čas:"
File diff suppressed because it is too large Load Diff
+33 -37
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-08-27 13:34+0000\n" "POT-Creation-Date: 2024-06-24 08:20+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -544,56 +544,52 @@ msgctxt "entry_status"
msgid "absent" msgid "absent"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:171 #: pretix/static/pretixcontrol/js/ui/editor.js:72
msgid "Check-in QR" msgid "Check-in QR"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:543 #: pretix/static/pretixcontrol/js/ui/editor.js:387
msgid "The PDF background file could not be loaded for the following reason:" msgid "The PDF background file could not be loaded for the following reason:"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:894 #: pretix/static/pretixcontrol/js/ui/editor.js:656
msgid "Group of objects" msgid "Group of objects"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:899 #: pretix/static/pretixcontrol/js/ui/editor.js:661
msgid "Text object (deprecated)" msgid "Text object"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:901 #: pretix/static/pretixcontrol/js/ui/editor.js:663
msgid "Text box"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:903
msgid "Barcode area" msgid "Barcode area"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:905 #: pretix/static/pretixcontrol/js/ui/editor.js:665
msgid "Image area" msgid "Image area"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:907 #: pretix/static/pretixcontrol/js/ui/editor.js:667
msgid "Powered by pretix" msgid "Powered by pretix"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:909 #: pretix/static/pretixcontrol/js/ui/editor.js:669
msgid "Object" msgid "Object"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:913 #: pretix/static/pretixcontrol/js/ui/editor.js:673
msgid "Ticket design" msgid "Ticket design"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:1250 #: pretix/static/pretixcontrol/js/ui/editor.js:972
msgid "Saving failed." msgid "Saving failed."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:1319 #: pretix/static/pretixcontrol/js/ui/editor.js:1041
#: pretix/static/pretixcontrol/js/ui/editor.js:1370 #: pretix/static/pretixcontrol/js/ui/editor.js:1091
msgid "Error while uploading your PDF file, please try again." msgid "Error while uploading your PDF file, please try again."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:1353 #: pretix/static/pretixcontrol/js/ui/editor.js:1074
msgid "Do you really want to leave the editor without saving your changes?" msgid "Do you really want to leave the editor without saving your changes?"
msgstr "" msgstr ""
@@ -609,54 +605,54 @@ msgstr ""
msgid "Unknown error." msgid "Unknown error."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:318 #: pretix/static/pretixcontrol/js/ui/main.js:313
msgid "Your color has great contrast and is very easy to read!" msgid "Your color has great contrast and is very easy to read!"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:322 #: pretix/static/pretixcontrol/js/ui/main.js:317
msgid "Your color has decent contrast and is probably good-enough to read!" msgid "Your color has decent contrast and is probably good-enough to read!"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:326 #: pretix/static/pretixcontrol/js/ui/main.js:321
msgid "" msgid ""
"Your color has bad contrast for text on white background, please choose a " "Your color has bad contrast for text on white background, please choose a "
"darker shade." "darker shade."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:491 #: pretix/static/pretixcontrol/js/ui/main.js:475
#: pretix/static/pretixcontrol/js/ui/main.js:511 #: pretix/static/pretixcontrol/js/ui/main.js:495
msgid "Search query" msgid "Search query"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:509 #: pretix/static/pretixcontrol/js/ui/main.js:493
msgid "All" msgid "All"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:510 #: pretix/static/pretixcontrol/js/ui/main.js:494
msgid "None" msgid "None"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:514 #: pretix/static/pretixcontrol/js/ui/main.js:498
msgid "Selected only" msgid "Selected only"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:847 #: pretix/static/pretixcontrol/js/ui/main.js:828
msgid "Enter page number between 1 and %(max)s." msgid "Enter page number between 1 and %(max)s."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:850 #: pretix/static/pretixcontrol/js/ui/main.js:831
msgid "Invalid page number." msgid "Invalid page number."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1008 #: pretix/static/pretixcontrol/js/ui/main.js:989
msgid "Use a different name internally" msgid "Use a different name internally"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1048 #: pretix/static/pretixcontrol/js/ui/main.js:1029
msgid "Click to close" msgid "Click to close"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1123 #: pretix/static/pretixcontrol/js/ui/main.js:1104
msgid "You have unsaved changes!" msgid "You have unsaved changes!"
msgstr "" msgstr ""
@@ -706,20 +702,20 @@ msgstr ""
msgid "Please enter the amount the organizer can keep." msgid "Please enter the amount the organizer can keep."
msgstr "" msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:449 #: pretix/static/pretixpresale/js/ui/main.js:444
msgid "Please enter a quantity for one of the ticket types." msgid "Please enter a quantity for one of the ticket types."
msgstr "" msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:485 #: pretix/static/pretixpresale/js/ui/main.js:480
msgid "required" msgid "required"
msgstr "" msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:588 #: pretix/static/pretixpresale/js/ui/main.js:583
#: pretix/static/pretixpresale/js/ui/main.js:607 #: pretix/static/pretixpresale/js/ui/main.js:602
msgid "Time zone:" msgid "Time zone:"
msgstr "" msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:598 #: pretix/static/pretixpresale/js/ui/main.js:593
msgid "Your local time:" msgid "Your local time:"
msgstr "" msgstr ""
File diff suppressed because it is too large Load Diff
+45 -53
View File
@@ -6,8 +6,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-08-27 13:34+0000\n" "POT-Creation-Date: 2024-06-24 08:20+0000\n"
"PO-Revision-Date: 2024-07-10 15:00+0000\n" "PO-Revision-Date: 2024-05-30 17:00+0000\n"
"Last-Translator: Nikolai <nikolai@lengefeldt.de>\n" "Last-Translator: Nikolai <nikolai@lengefeldt.de>\n"
"Language-Team: Danish <https://translate.pretix.eu/projects/pretix/pretix-js/" "Language-Team: Danish <https://translate.pretix.eu/projects/pretix/pretix-js/"
"da/>\n" "da/>\n"
@@ -16,7 +16,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n" "Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.6.1\n" "X-Generator: Weblate 5.5.5\n"
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56 #: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62 #: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
@@ -261,7 +261,7 @@ msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:51 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:51
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:60 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:60
msgid "Ticket not paid" msgid "Ticket not paid"
msgstr "Billet ikke betalt" msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:52 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:52
msgid "This ticket is not yet paid. Do you want to continue anyways?" msgid "This ticket is not yet paid. Do you want to continue anyways?"
@@ -281,11 +281,11 @@ msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:56 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:56
msgid "Ticket already used" msgid "Ticket already used"
msgstr "Billet allerede i brug" msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:57 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:57
msgid "Information required" msgid "Information required"
msgstr "Kræver information" msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:58 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:58
msgid "Unknown ticket" msgid "Unknown ticket"
@@ -301,15 +301,15 @@ msgstr "Adgang ikke tilladt"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:62 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:62
msgid "Ticket code revoked/changed" msgid "Ticket code revoked/changed"
msgstr "Billetkode trukket tilbage/ændret" msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:63 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:63
msgid "Ticket blocked" msgid "Ticket blocked"
msgstr "Billet blokeret" msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:64 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:64
msgid "Ticket not valid at this time" msgid "Ticket not valid at this time"
msgstr "Billetten er ikke gyldig på dette tidspunkt" msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:65 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:65
msgid "Order canceled" msgid "Order canceled"
@@ -317,11 +317,11 @@ msgstr "Bestilling annulleret"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:66 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:66
msgid "Ticket code is ambiguous on list" msgid "Ticket code is ambiguous on list"
msgstr "Billetkoden er flertydig på listen" msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:67 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:67
msgid "Order not approved" msgid "Order not approved"
msgstr "Bestilling ikke godkendt" msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:68 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:68
#, fuzzy #, fuzzy
@@ -472,7 +472,7 @@ msgstr "Produktvariation"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:107 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:107
msgid "Gate" msgid "Gate"
msgstr "Indgang" msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:111 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:111
msgid "Current date and time" msgid "Current date and time"
@@ -548,7 +548,7 @@ msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:186 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:186
msgid "Event admission" msgid "Event admission"
msgstr "Adgang til arrangementet" msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:187 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:187
msgid "custom date and time" msgid "custom date and time"
@@ -584,62 +584,54 @@ msgctxt "entry_status"
msgid "absent" msgid "absent"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:171 #: pretix/static/pretixcontrol/js/ui/editor.js:72
msgid "Check-in QR" msgid "Check-in QR"
msgstr "Check-in QR" msgstr "Check-in QR"
#: pretix/static/pretixcontrol/js/ui/editor.js:543 #: pretix/static/pretixcontrol/js/ui/editor.js:387
msgid "The PDF background file could not be loaded for the following reason:" msgid "The PDF background file could not be loaded for the following reason:"
msgstr "Baggrunds-pdf'en kunne ikke hentes af følgende grund:" msgstr "Baggrunds-pdf'en kunne ikke hentes af følgende grund:"
#: pretix/static/pretixcontrol/js/ui/editor.js:894 #: pretix/static/pretixcontrol/js/ui/editor.js:656
msgid "Group of objects" msgid "Group of objects"
msgstr "Gruppe af objekter" msgstr "Gruppe af objekter"
#: pretix/static/pretixcontrol/js/ui/editor.js:899 #: pretix/static/pretixcontrol/js/ui/editor.js:661
#, fuzzy msgid "Text object"
#| msgid "Text object"
msgid "Text object (deprecated)"
msgstr "Tekstobjekt" msgstr "Tekstobjekt"
#: pretix/static/pretixcontrol/js/ui/editor.js:901 #: pretix/static/pretixcontrol/js/ui/editor.js:663
#, fuzzy
#| msgid "Text object"
msgid "Text box"
msgstr "Tekstobjekt"
#: pretix/static/pretixcontrol/js/ui/editor.js:903
msgid "Barcode area" msgid "Barcode area"
msgstr "QR-kode-område" msgstr "QR-kode-område"
#: pretix/static/pretixcontrol/js/ui/editor.js:905 #: pretix/static/pretixcontrol/js/ui/editor.js:665
#, fuzzy #, fuzzy
#| msgid "Barcode area" #| msgid "Barcode area"
msgid "Image area" msgid "Image area"
msgstr "QR-kode-område" msgstr "QR-kode-område"
#: pretix/static/pretixcontrol/js/ui/editor.js:907 #: pretix/static/pretixcontrol/js/ui/editor.js:667
msgid "Powered by pretix" msgid "Powered by pretix"
msgstr "Drevet af pretix" msgstr "Drevet af pretix"
#: pretix/static/pretixcontrol/js/ui/editor.js:909 #: pretix/static/pretixcontrol/js/ui/editor.js:669
msgid "Object" msgid "Object"
msgstr "Objekt" msgstr "Objekt"
#: pretix/static/pretixcontrol/js/ui/editor.js:913 #: pretix/static/pretixcontrol/js/ui/editor.js:673
msgid "Ticket design" msgid "Ticket design"
msgstr "Billetdesign" msgstr "Billetdesign"
#: pretix/static/pretixcontrol/js/ui/editor.js:1250 #: pretix/static/pretixcontrol/js/ui/editor.js:972
msgid "Saving failed." msgid "Saving failed."
msgstr "Gem fejlede." msgstr "Gem fejlede."
#: pretix/static/pretixcontrol/js/ui/editor.js:1319 #: pretix/static/pretixcontrol/js/ui/editor.js:1041
#: pretix/static/pretixcontrol/js/ui/editor.js:1370 #: pretix/static/pretixcontrol/js/ui/editor.js:1091
msgid "Error while uploading your PDF file, please try again." msgid "Error while uploading your PDF file, please try again."
msgstr "Fejl under upload af pdf. Prøv venligt igen." msgstr "Fejl under upload af pdf. Prøv venligt igen."
#: pretix/static/pretixcontrol/js/ui/editor.js:1353 #: pretix/static/pretixcontrol/js/ui/editor.js:1074
msgid "Do you really want to leave the editor without saving your changes?" msgid "Do you really want to leave the editor without saving your changes?"
msgstr "" msgstr ""
"Er du sikker på at du vil forlade editoren uden at gemme dine ændringer?" "Er du sikker på at du vil forlade editoren uden at gemme dine ændringer?"
@@ -656,54 +648,54 @@ msgstr "Opretter beskeder …"
msgid "Unknown error." msgid "Unknown error."
msgstr "Ukendt fejl." msgstr "Ukendt fejl."
#: pretix/static/pretixcontrol/js/ui/main.js:318 #: pretix/static/pretixcontrol/js/ui/main.js:313
msgid "Your color has great contrast and is very easy to read!" msgid "Your color has great contrast and is very easy to read!"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:322 #: pretix/static/pretixcontrol/js/ui/main.js:317
msgid "Your color has decent contrast and is probably good-enough to read!" msgid "Your color has decent contrast and is probably good-enough to read!"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:326 #: pretix/static/pretixcontrol/js/ui/main.js:321
msgid "" msgid ""
"Your color has bad contrast for text on white background, please choose a " "Your color has bad contrast for text on white background, please choose a "
"darker shade." "darker shade."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:491 #: pretix/static/pretixcontrol/js/ui/main.js:475
#: pretix/static/pretixcontrol/js/ui/main.js:511 #: pretix/static/pretixcontrol/js/ui/main.js:495
msgid "Search query" msgid "Search query"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:509 #: pretix/static/pretixcontrol/js/ui/main.js:493
msgid "All" msgid "All"
msgstr "Alle" msgstr "Alle"
#: pretix/static/pretixcontrol/js/ui/main.js:510 #: pretix/static/pretixcontrol/js/ui/main.js:494
msgid "None" msgid "None"
msgstr "Ingen" msgstr "Ingen"
#: pretix/static/pretixcontrol/js/ui/main.js:514 #: pretix/static/pretixcontrol/js/ui/main.js:498
msgid "Selected only" msgid "Selected only"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:847 #: pretix/static/pretixcontrol/js/ui/main.js:828
msgid "Enter page number between 1 and %(max)s." msgid "Enter page number between 1 and %(max)s."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:850 #: pretix/static/pretixcontrol/js/ui/main.js:831
msgid "Invalid page number." msgid "Invalid page number."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1008 #: pretix/static/pretixcontrol/js/ui/main.js:989
msgid "Use a different name internally" msgid "Use a different name internally"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1048 #: pretix/static/pretixcontrol/js/ui/main.js:1029
msgid "Click to close" msgid "Click to close"
msgstr "Klik for at lukke" msgstr "Klik for at lukke"
#: pretix/static/pretixcontrol/js/ui/main.js:1123 #: pretix/static/pretixcontrol/js/ui/main.js:1104
msgid "You have unsaved changes!" msgid "You have unsaved changes!"
msgstr "Du har ændringer, der ikke er gemt!" msgstr "Du har ændringer, der ikke er gemt!"
@@ -763,22 +755,22 @@ msgstr "fra %(currency)s %(price)s"
msgid "Please enter the amount the organizer can keep." msgid "Please enter the amount the organizer can keep."
msgstr "" msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:449 #: pretix/static/pretixpresale/js/ui/main.js:444
msgid "Please enter a quantity for one of the ticket types." msgid "Please enter a quantity for one of the ticket types."
msgstr "" msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:485 #: pretix/static/pretixpresale/js/ui/main.js:480
#, fuzzy #, fuzzy
#| msgid "Cart expired" #| msgid "Cart expired"
msgid "required" msgid "required"
msgstr "Kurv udløbet" msgstr "Kurv udløbet"
#: pretix/static/pretixpresale/js/ui/main.js:588 #: pretix/static/pretixpresale/js/ui/main.js:583
#: pretix/static/pretixpresale/js/ui/main.js:607 #: pretix/static/pretixpresale/js/ui/main.js:602
msgid "Time zone:" msgid "Time zone:"
msgstr "Tidszone:" msgstr "Tidszone:"
#: pretix/static/pretixpresale/js/ui/main.js:598 #: pretix/static/pretixpresale/js/ui/main.js:593
msgid "Your local time:" msgid "Your local time:"
msgstr "Din lokaltid:" msgstr "Din lokaltid:"
File diff suppressed because it is too large Load Diff
+35 -41
View File
@@ -7,8 +7,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-08-27 13:34+0000\n" "POT-Creation-Date: 2024-06-24 08:20+0000\n"
"PO-Revision-Date: 2024-08-27 16:02+0000\n" "PO-Revision-Date: 2024-06-30 21:07+0000\n"
"Last-Translator: Raphael Michel <michel@rami.io>\n" "Last-Translator: Raphael Michel <michel@rami.io>\n"
"Language-Team: German <https://translate.pretix.eu/projects/pretix/pretix-js/" "Language-Team: German <https://translate.pretix.eu/projects/pretix/pretix-js/"
"de/>\n" "de/>\n"
@@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n" "Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.7\n" "X-Generator: Weblate 5.6.1\n"
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56 #: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62 #: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
@@ -559,59 +559,53 @@ msgctxt "entry_status"
msgid "absent" msgid "absent"
msgstr "abwesend" msgstr "abwesend"
#: pretix/static/pretixcontrol/js/ui/editor.js:171 #: pretix/static/pretixcontrol/js/ui/editor.js:72
msgid "Check-in QR" msgid "Check-in QR"
msgstr "Check-in-QR-Code" msgstr "Check-in-QR-Code"
#: pretix/static/pretixcontrol/js/ui/editor.js:543 #: pretix/static/pretixcontrol/js/ui/editor.js:387
msgid "The PDF background file could not be loaded for the following reason:" msgid "The PDF background file could not be loaded for the following reason:"
msgstr "Die Hintergrund-PDF-Datei konnte nicht geladen werden:" msgstr "Die Hintergrund-PDF-Datei konnte nicht geladen werden:"
#: pretix/static/pretixcontrol/js/ui/editor.js:894 #: pretix/static/pretixcontrol/js/ui/editor.js:656
msgid "Group of objects" msgid "Group of objects"
msgstr "Gruppe von Objekten" msgstr "Gruppe von Objekten"
#: pretix/static/pretixcontrol/js/ui/editor.js:899 #: pretix/static/pretixcontrol/js/ui/editor.js:661
#, fuzzy msgid "Text object"
#| msgid "Text object"
msgid "Text object (deprecated)"
msgstr "Text-Objekt" msgstr "Text-Objekt"
#: pretix/static/pretixcontrol/js/ui/editor.js:901 #: pretix/static/pretixcontrol/js/ui/editor.js:663
msgid "Text box"
msgstr "Textbox"
#: pretix/static/pretixcontrol/js/ui/editor.js:903
msgid "Barcode area" msgid "Barcode area"
msgstr "QR-Code-Bereich" msgstr "QR-Code-Bereich"
#: pretix/static/pretixcontrol/js/ui/editor.js:905 #: pretix/static/pretixcontrol/js/ui/editor.js:665
msgid "Image area" msgid "Image area"
msgstr "Bildbereich" msgstr "Bildbereich"
#: pretix/static/pretixcontrol/js/ui/editor.js:907 #: pretix/static/pretixcontrol/js/ui/editor.js:667
msgid "Powered by pretix" msgid "Powered by pretix"
msgstr "Event-Ticketshop von pretix" msgstr "Event-Ticketshop von pretix"
#: pretix/static/pretixcontrol/js/ui/editor.js:909 #: pretix/static/pretixcontrol/js/ui/editor.js:669
msgid "Object" msgid "Object"
msgstr "Objekt" msgstr "Objekt"
#: pretix/static/pretixcontrol/js/ui/editor.js:913 #: pretix/static/pretixcontrol/js/ui/editor.js:673
msgid "Ticket design" msgid "Ticket design"
msgstr "Ticket-Design" msgstr "Ticket-Design"
#: pretix/static/pretixcontrol/js/ui/editor.js:1250 #: pretix/static/pretixcontrol/js/ui/editor.js:972
msgid "Saving failed." msgid "Saving failed."
msgstr "Speichern fehlgeschlagen." msgstr "Speichern fehlgeschlagen."
#: pretix/static/pretixcontrol/js/ui/editor.js:1319 #: pretix/static/pretixcontrol/js/ui/editor.js:1041
#: pretix/static/pretixcontrol/js/ui/editor.js:1370 #: pretix/static/pretixcontrol/js/ui/editor.js:1091
msgid "Error while uploading your PDF file, please try again." msgid "Error while uploading your PDF file, please try again."
msgstr "" msgstr ""
"Es gab ein Problem beim Hochladen der PDF-Datei, bitte erneut versuchen." "Es gab ein Problem beim Hochladen der PDF-Datei, bitte erneut versuchen."
#: pretix/static/pretixcontrol/js/ui/editor.js:1353 #: pretix/static/pretixcontrol/js/ui/editor.js:1074
msgid "Do you really want to leave the editor without saving your changes?" msgid "Do you really want to leave the editor without saving your changes?"
msgstr "" msgstr ""
"Möchten Sie den Editor wirklich schließen ohne Ihre Änderungen zu speichern?" "Möchten Sie den Editor wirklich schließen ohne Ihre Änderungen zu speichern?"
@@ -628,17 +622,17 @@ msgstr "Generiere Nachrichten…"
msgid "Unknown error." msgid "Unknown error."
msgstr "Unbekannter Fehler." msgstr "Unbekannter Fehler."
#: pretix/static/pretixcontrol/js/ui/main.js:318 #: pretix/static/pretixcontrol/js/ui/main.js:313
msgid "Your color has great contrast and is very easy to read!" msgid "Your color has great contrast and is very easy to read!"
msgstr "Diese Farbe hat einen sehr guten Kontrast und ist sehr gut zu lesen!" msgstr "Diese Farbe hat einen sehr guten Kontrast und ist sehr gut zu lesen!"
#: pretix/static/pretixcontrol/js/ui/main.js:322 #: pretix/static/pretixcontrol/js/ui/main.js:317
msgid "Your color has decent contrast and is probably good-enough to read!" msgid "Your color has decent contrast and is probably good-enough to read!"
msgstr "" msgstr ""
"Diese Farbe hat einen ausreichenden Kontrast und ist wahrscheinlich gut zu " "Diese Farbe hat einen ausreichenden Kontrast und ist wahrscheinlich gut zu "
"lesen!" "lesen!"
#: pretix/static/pretixcontrol/js/ui/main.js:326 #: pretix/static/pretixcontrol/js/ui/main.js:321
msgid "" msgid ""
"Your color has bad contrast for text on white background, please choose a " "Your color has bad contrast for text on white background, please choose a "
"darker shade." "darker shade."
@@ -646,40 +640,40 @@ msgstr ""
"Diese Farbe hat einen schlechten Kontrast für Text auf einem weißen " "Diese Farbe hat einen schlechten Kontrast für Text auf einem weißen "
"Hintergrund. Bitte wählen Sie eine dunklere Farbe." "Hintergrund. Bitte wählen Sie eine dunklere Farbe."
#: pretix/static/pretixcontrol/js/ui/main.js:491 #: pretix/static/pretixcontrol/js/ui/main.js:475
#: pretix/static/pretixcontrol/js/ui/main.js:511 #: pretix/static/pretixcontrol/js/ui/main.js:495
msgid "Search query" msgid "Search query"
msgstr "Suchbegriff" msgstr "Suchbegriff"
#: pretix/static/pretixcontrol/js/ui/main.js:509 #: pretix/static/pretixcontrol/js/ui/main.js:493
msgid "All" msgid "All"
msgstr "Alle" msgstr "Alle"
#: pretix/static/pretixcontrol/js/ui/main.js:510 #: pretix/static/pretixcontrol/js/ui/main.js:494
msgid "None" msgid "None"
msgstr "Keine" msgstr "Keine"
#: pretix/static/pretixcontrol/js/ui/main.js:514 #: pretix/static/pretixcontrol/js/ui/main.js:498
msgid "Selected only" msgid "Selected only"
msgstr "Nur ausgewählte" msgstr "Nur ausgewählte"
#: pretix/static/pretixcontrol/js/ui/main.js:847 #: pretix/static/pretixcontrol/js/ui/main.js:828
msgid "Enter page number between 1 and %(max)s." msgid "Enter page number between 1 and %(max)s."
msgstr "Geben Sie eine Seitenzahl zwischen 1 und %(max)s ein." msgstr "Geben Sie eine Seitenzahl zwischen 1 und %(max)s ein."
#: pretix/static/pretixcontrol/js/ui/main.js:850 #: pretix/static/pretixcontrol/js/ui/main.js:831
msgid "Invalid page number." msgid "Invalid page number."
msgstr "Ungültige Seitenzahl." msgstr "Ungültige Seitenzahl."
#: pretix/static/pretixcontrol/js/ui/main.js:1008 #: pretix/static/pretixcontrol/js/ui/main.js:989
msgid "Use a different name internally" msgid "Use a different name internally"
msgstr "Intern einen anderen Namen verwenden" msgstr "Intern einen anderen Namen verwenden"
#: pretix/static/pretixcontrol/js/ui/main.js:1048 #: pretix/static/pretixcontrol/js/ui/main.js:1029
msgid "Click to close" msgid "Click to close"
msgstr "Klicken zum Schließen" msgstr "Klicken zum Schließen"
#: pretix/static/pretixcontrol/js/ui/main.js:1123 #: pretix/static/pretixcontrol/js/ui/main.js:1104
msgid "You have unsaved changes!" msgid "You have unsaved changes!"
msgstr "Sie haben ungespeicherte Änderungen!" msgstr "Sie haben ungespeicherte Änderungen!"
@@ -734,20 +728,20 @@ msgstr "Sie erhalten %(currency)s %(amount)s zurück"
msgid "Please enter the amount the organizer can keep." msgid "Please enter the amount the organizer can keep."
msgstr "Bitte geben Sie den Betrag ein, den der Veranstalter einbehalten darf." msgstr "Bitte geben Sie den Betrag ein, den der Veranstalter einbehalten darf."
#: pretix/static/pretixpresale/js/ui/main.js:449 #: pretix/static/pretixpresale/js/ui/main.js:444
msgid "Please enter a quantity for one of the ticket types." msgid "Please enter a quantity for one of the ticket types."
msgstr "Bitte tragen Sie eine Menge für eines der Produkte ein." msgstr "Bitte tragen Sie eine Menge für eines der Produkte ein."
#: pretix/static/pretixpresale/js/ui/main.js:485 #: pretix/static/pretixpresale/js/ui/main.js:480
msgid "required" msgid "required"
msgstr "verpflichtend" msgstr "verpflichtend"
#: pretix/static/pretixpresale/js/ui/main.js:588 #: pretix/static/pretixpresale/js/ui/main.js:583
#: pretix/static/pretixpresale/js/ui/main.js:607 #: pretix/static/pretixpresale/js/ui/main.js:602
msgid "Time zone:" msgid "Time zone:"
msgstr "Zeitzone:" msgstr "Zeitzone:"
#: pretix/static/pretixpresale/js/ui/main.js:598 #: pretix/static/pretixpresale/js/ui/main.js:593
msgid "Your local time:" msgid "Your local time:"
msgstr "Deine lokale Zeit:" msgstr "Deine lokale Zeit:"
-1
View File
@@ -259,7 +259,6 @@ Requests
Reservierungszeitraum Reservierungszeitraum
reverse reverse
Revisionssicherheit Revisionssicherheit
Revolut
rückabgewickelt rückabgewickelt
Rundungsdifferenzen Rundungsdifferenzen
Sa Sa
File diff suppressed because it is too large Load Diff
@@ -7,8 +7,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-08-27 13:34+0000\n" "POT-Creation-Date: 2024-06-24 08:20+0000\n"
"PO-Revision-Date: 2024-08-27 16:00+0000\n" "PO-Revision-Date: 2024-06-30 21:07+0000\n"
"Last-Translator: Raphael Michel <michel@rami.io>\n" "Last-Translator: Raphael Michel <michel@rami.io>\n"
"Language-Team: German (informal) <https://translate.pretix.eu/projects/" "Language-Team: German (informal) <https://translate.pretix.eu/projects/"
"pretix/pretix-js/de_Informal/>\n" "pretix/pretix-js/de_Informal/>\n"
@@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n" "Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.7\n" "X-Generator: Weblate 5.6.1\n"
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56 #: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62 #: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
@@ -558,59 +558,53 @@ msgctxt "entry_status"
msgid "absent" msgid "absent"
msgstr "abwesend" msgstr "abwesend"
#: pretix/static/pretixcontrol/js/ui/editor.js:171 #: pretix/static/pretixcontrol/js/ui/editor.js:72
msgid "Check-in QR" msgid "Check-in QR"
msgstr "Check-in-QR-Code" msgstr "Check-in-QR-Code"
#: pretix/static/pretixcontrol/js/ui/editor.js:543 #: pretix/static/pretixcontrol/js/ui/editor.js:387
msgid "The PDF background file could not be loaded for the following reason:" msgid "The PDF background file could not be loaded for the following reason:"
msgstr "Die Hintergrund-PDF-Datei konnte nicht geladen werden:" msgstr "Die Hintergrund-PDF-Datei konnte nicht geladen werden:"
#: pretix/static/pretixcontrol/js/ui/editor.js:894 #: pretix/static/pretixcontrol/js/ui/editor.js:656
msgid "Group of objects" msgid "Group of objects"
msgstr "Gruppe von Objekten" msgstr "Gruppe von Objekten"
#: pretix/static/pretixcontrol/js/ui/editor.js:899 #: pretix/static/pretixcontrol/js/ui/editor.js:661
#, fuzzy msgid "Text object"
#| msgid "Text object"
msgid "Text object (deprecated)"
msgstr "Text-Objekt" msgstr "Text-Objekt"
#: pretix/static/pretixcontrol/js/ui/editor.js:901 #: pretix/static/pretixcontrol/js/ui/editor.js:663
msgid "Text box"
msgstr "Textbox"
#: pretix/static/pretixcontrol/js/ui/editor.js:903
msgid "Barcode area" msgid "Barcode area"
msgstr "QR-Code-Bereich" msgstr "QR-Code-Bereich"
#: pretix/static/pretixcontrol/js/ui/editor.js:905 #: pretix/static/pretixcontrol/js/ui/editor.js:665
msgid "Image area" msgid "Image area"
msgstr "Bildbereich" msgstr "Bildbereich"
#: pretix/static/pretixcontrol/js/ui/editor.js:907 #: pretix/static/pretixcontrol/js/ui/editor.js:667
msgid "Powered by pretix" msgid "Powered by pretix"
msgstr "Event-Ticketshop von pretix" msgstr "Event-Ticketshop von pretix"
#: pretix/static/pretixcontrol/js/ui/editor.js:909 #: pretix/static/pretixcontrol/js/ui/editor.js:669
msgid "Object" msgid "Object"
msgstr "Objekt" msgstr "Objekt"
#: pretix/static/pretixcontrol/js/ui/editor.js:913 #: pretix/static/pretixcontrol/js/ui/editor.js:673
msgid "Ticket design" msgid "Ticket design"
msgstr "Ticket-Design" msgstr "Ticket-Design"
#: pretix/static/pretixcontrol/js/ui/editor.js:1250 #: pretix/static/pretixcontrol/js/ui/editor.js:972
msgid "Saving failed." msgid "Saving failed."
msgstr "Speichern fehlgeschlagen." msgstr "Speichern fehlgeschlagen."
#: pretix/static/pretixcontrol/js/ui/editor.js:1319 #: pretix/static/pretixcontrol/js/ui/editor.js:1041
#: pretix/static/pretixcontrol/js/ui/editor.js:1370 #: pretix/static/pretixcontrol/js/ui/editor.js:1091
msgid "Error while uploading your PDF file, please try again." msgid "Error while uploading your PDF file, please try again."
msgstr "" msgstr ""
"Es gab ein Problem beim Hochladen der PDF-Datei, bitte erneut versuchen." "Es gab ein Problem beim Hochladen der PDF-Datei, bitte erneut versuchen."
#: pretix/static/pretixcontrol/js/ui/editor.js:1353 #: pretix/static/pretixcontrol/js/ui/editor.js:1074
msgid "Do you really want to leave the editor without saving your changes?" msgid "Do you really want to leave the editor without saving your changes?"
msgstr "" msgstr ""
"Möchtest du den Editor wirklich schließen ohne Ihre Änderungen zu speichern?" "Möchtest du den Editor wirklich schließen ohne Ihre Änderungen zu speichern?"
@@ -627,17 +621,17 @@ msgstr "Generiere Nachrichten…"
msgid "Unknown error." msgid "Unknown error."
msgstr "Unbekannter Fehler." msgstr "Unbekannter Fehler."
#: pretix/static/pretixcontrol/js/ui/main.js:318 #: pretix/static/pretixcontrol/js/ui/main.js:313
msgid "Your color has great contrast and is very easy to read!" msgid "Your color has great contrast and is very easy to read!"
msgstr "Diese Farbe hat einen sehr guten Kontrast und ist sehr gut zu lesen!" msgstr "Diese Farbe hat einen sehr guten Kontrast und ist sehr gut zu lesen!"
#: pretix/static/pretixcontrol/js/ui/main.js:322 #: pretix/static/pretixcontrol/js/ui/main.js:317
msgid "Your color has decent contrast and is probably good-enough to read!" msgid "Your color has decent contrast and is probably good-enough to read!"
msgstr "" msgstr ""
"Diese Farbe hat einen ausreichenden Kontrast und ist wahrscheinlich gut zu " "Diese Farbe hat einen ausreichenden Kontrast und ist wahrscheinlich gut zu "
"lesen!" "lesen!"
#: pretix/static/pretixcontrol/js/ui/main.js:326 #: pretix/static/pretixcontrol/js/ui/main.js:321
msgid "" msgid ""
"Your color has bad contrast for text on white background, please choose a " "Your color has bad contrast for text on white background, please choose a "
"darker shade." "darker shade."
@@ -645,40 +639,40 @@ msgstr ""
"Diese Farbe hat einen schlechten Kontrast für Text auf einem weißen " "Diese Farbe hat einen schlechten Kontrast für Text auf einem weißen "
"Hintergrund. Bitte wähle eine dunklere Farbe." "Hintergrund. Bitte wähle eine dunklere Farbe."
#: pretix/static/pretixcontrol/js/ui/main.js:491 #: pretix/static/pretixcontrol/js/ui/main.js:475
#: pretix/static/pretixcontrol/js/ui/main.js:511 #: pretix/static/pretixcontrol/js/ui/main.js:495
msgid "Search query" msgid "Search query"
msgstr "Suchbegriff" msgstr "Suchbegriff"
#: pretix/static/pretixcontrol/js/ui/main.js:509 #: pretix/static/pretixcontrol/js/ui/main.js:493
msgid "All" msgid "All"
msgstr "Alle" msgstr "Alle"
#: pretix/static/pretixcontrol/js/ui/main.js:510 #: pretix/static/pretixcontrol/js/ui/main.js:494
msgid "None" msgid "None"
msgstr "Keine" msgstr "Keine"
#: pretix/static/pretixcontrol/js/ui/main.js:514 #: pretix/static/pretixcontrol/js/ui/main.js:498
msgid "Selected only" msgid "Selected only"
msgstr "Nur ausgewählte" msgstr "Nur ausgewählte"
#: pretix/static/pretixcontrol/js/ui/main.js:847 #: pretix/static/pretixcontrol/js/ui/main.js:828
msgid "Enter page number between 1 and %(max)s." msgid "Enter page number between 1 and %(max)s."
msgstr "Gib eine Seitenzahl zwischen 1 und %(max)s ein." msgstr "Gib eine Seitenzahl zwischen 1 und %(max)s ein."
#: pretix/static/pretixcontrol/js/ui/main.js:850 #: pretix/static/pretixcontrol/js/ui/main.js:831
msgid "Invalid page number." msgid "Invalid page number."
msgstr "Ungültige Seitenzahl." msgstr "Ungültige Seitenzahl."
#: pretix/static/pretixcontrol/js/ui/main.js:1008 #: pretix/static/pretixcontrol/js/ui/main.js:989
msgid "Use a different name internally" msgid "Use a different name internally"
msgstr "Intern einen anderen Namen verwenden" msgstr "Intern einen anderen Namen verwenden"
#: pretix/static/pretixcontrol/js/ui/main.js:1048 #: pretix/static/pretixcontrol/js/ui/main.js:1029
msgid "Click to close" msgid "Click to close"
msgstr "Klicken zum Schließen" msgstr "Klicken zum Schließen"
#: pretix/static/pretixcontrol/js/ui/main.js:1123 #: pretix/static/pretixcontrol/js/ui/main.js:1104
msgid "You have unsaved changes!" msgid "You have unsaved changes!"
msgstr "Du hast ungespeicherte Änderungen!" msgstr "Du hast ungespeicherte Änderungen!"
@@ -733,20 +727,20 @@ msgstr "Du erhältst %(currency)s %(amount)s zurück"
msgid "Please enter the amount the organizer can keep." msgid "Please enter the amount the organizer can keep."
msgstr "Bitte gib den Betrag ein, den der Veranstalter einbehalten darf." msgstr "Bitte gib den Betrag ein, den der Veranstalter einbehalten darf."
#: pretix/static/pretixpresale/js/ui/main.js:449 #: pretix/static/pretixpresale/js/ui/main.js:444
msgid "Please enter a quantity for one of the ticket types." msgid "Please enter a quantity for one of the ticket types."
msgstr "Bitte trage eine Menge für eines der Produkte ein." msgstr "Bitte trage eine Menge für eines der Produkte ein."
#: pretix/static/pretixpresale/js/ui/main.js:485 #: pretix/static/pretixpresale/js/ui/main.js:480
msgid "required" msgid "required"
msgstr "verpflichtend" msgstr "verpflichtend"
#: pretix/static/pretixpresale/js/ui/main.js:588 #: pretix/static/pretixpresale/js/ui/main.js:583
#: pretix/static/pretixpresale/js/ui/main.js:607 #: pretix/static/pretixpresale/js/ui/main.js:602
msgid "Time zone:" msgid "Time zone:"
msgstr "Zeitzone:" msgstr "Zeitzone:"
#: pretix/static/pretixpresale/js/ui/main.js:598 #: pretix/static/pretixpresale/js/ui/main.js:593
msgid "Your local time:" msgid "Your local time:"
msgstr "Deine lokale Zeit:" msgstr "Deine lokale Zeit:"
@@ -259,7 +259,6 @@ Requests
Reservierungszeitraum Reservierungszeitraum
reverse reverse
Revisionssicherheit Revisionssicherheit
Revolut
rückabgewickelt rückabgewickelt
Rundungsdifferenzen Rundungsdifferenzen
Sa Sa
+1998 -2247
View File
File diff suppressed because it is too large Load Diff
+33 -37
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-08-27 13:34+0000\n" "POT-Creation-Date: 2024-06-30 18:56+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -543,56 +543,52 @@ msgctxt "entry_status"
msgid "absent" msgid "absent"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:171 #: pretix/static/pretixcontrol/js/ui/editor.js:72
msgid "Check-in QR" msgid "Check-in QR"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:543 #: pretix/static/pretixcontrol/js/ui/editor.js:387
msgid "The PDF background file could not be loaded for the following reason:" msgid "The PDF background file could not be loaded for the following reason:"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:894 #: pretix/static/pretixcontrol/js/ui/editor.js:656
msgid "Group of objects" msgid "Group of objects"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:899 #: pretix/static/pretixcontrol/js/ui/editor.js:661
msgid "Text object (deprecated)" msgid "Text object"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:901 #: pretix/static/pretixcontrol/js/ui/editor.js:663
msgid "Text box"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:903
msgid "Barcode area" msgid "Barcode area"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:905 #: pretix/static/pretixcontrol/js/ui/editor.js:665
msgid "Image area" msgid "Image area"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:907 #: pretix/static/pretixcontrol/js/ui/editor.js:667
msgid "Powered by pretix" msgid "Powered by pretix"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:909 #: pretix/static/pretixcontrol/js/ui/editor.js:669
msgid "Object" msgid "Object"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:913 #: pretix/static/pretixcontrol/js/ui/editor.js:673
msgid "Ticket design" msgid "Ticket design"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:1250 #: pretix/static/pretixcontrol/js/ui/editor.js:972
msgid "Saving failed." msgid "Saving failed."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:1319 #: pretix/static/pretixcontrol/js/ui/editor.js:1041
#: pretix/static/pretixcontrol/js/ui/editor.js:1370 #: pretix/static/pretixcontrol/js/ui/editor.js:1091
msgid "Error while uploading your PDF file, please try again." msgid "Error while uploading your PDF file, please try again."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:1353 #: pretix/static/pretixcontrol/js/ui/editor.js:1074
msgid "Do you really want to leave the editor without saving your changes?" msgid "Do you really want to leave the editor without saving your changes?"
msgstr "" msgstr ""
@@ -608,54 +604,54 @@ msgstr ""
msgid "Unknown error." msgid "Unknown error."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:318 #: pretix/static/pretixcontrol/js/ui/main.js:313
msgid "Your color has great contrast and is very easy to read!" msgid "Your color has great contrast and is very easy to read!"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:322 #: pretix/static/pretixcontrol/js/ui/main.js:317
msgid "Your color has decent contrast and is probably good-enough to read!" msgid "Your color has decent contrast and is probably good-enough to read!"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:326 #: pretix/static/pretixcontrol/js/ui/main.js:321
msgid "" msgid ""
"Your color has bad contrast for text on white background, please choose a " "Your color has bad contrast for text on white background, please choose a "
"darker shade." "darker shade."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:491 #: pretix/static/pretixcontrol/js/ui/main.js:475
#: pretix/static/pretixcontrol/js/ui/main.js:511 #: pretix/static/pretixcontrol/js/ui/main.js:495
msgid "Search query" msgid "Search query"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:509 #: pretix/static/pretixcontrol/js/ui/main.js:493
msgid "All" msgid "All"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:510 #: pretix/static/pretixcontrol/js/ui/main.js:494
msgid "None" msgid "None"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:514 #: pretix/static/pretixcontrol/js/ui/main.js:498
msgid "Selected only" msgid "Selected only"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:847 #: pretix/static/pretixcontrol/js/ui/main.js:828
msgid "Enter page number between 1 and %(max)s." msgid "Enter page number between 1 and %(max)s."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:850 #: pretix/static/pretixcontrol/js/ui/main.js:831
msgid "Invalid page number." msgid "Invalid page number."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1008 #: pretix/static/pretixcontrol/js/ui/main.js:989
msgid "Use a different name internally" msgid "Use a different name internally"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1048 #: pretix/static/pretixcontrol/js/ui/main.js:1029
msgid "Click to close" msgid "Click to close"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1123 #: pretix/static/pretixcontrol/js/ui/main.js:1104
msgid "You have unsaved changes!" msgid "You have unsaved changes!"
msgstr "" msgstr ""
@@ -705,20 +701,20 @@ msgstr ""
msgid "Please enter the amount the organizer can keep." msgid "Please enter the amount the organizer can keep."
msgstr "" msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:449 #: pretix/static/pretixpresale/js/ui/main.js:444
msgid "Please enter a quantity for one of the ticket types." msgid "Please enter a quantity for one of the ticket types."
msgstr "" msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:485 #: pretix/static/pretixpresale/js/ui/main.js:480
msgid "required" msgid "required"
msgstr "" msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:588 #: pretix/static/pretixpresale/js/ui/main.js:583
#: pretix/static/pretixpresale/js/ui/main.js:607 #: pretix/static/pretixpresale/js/ui/main.js:602
msgid "Time zone:" msgid "Time zone:"
msgstr "" msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:598 #: pretix/static/pretixpresale/js/ui/main.js:593
msgid "Your local time:" msgid "Your local time:"
msgstr "" msgstr ""
File diff suppressed because it is too large Load Diff
+33 -41
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-08-27 13:34+0000\n" "POT-Creation-Date: 2024-06-24 08:20+0000\n"
"PO-Revision-Date: 2019-10-03 19:00+0000\n" "PO-Revision-Date: 2019-10-03 19:00+0000\n"
"Last-Translator: Chris Spy <chrispiropoulou@hotmail.com>\n" "Last-Translator: Chris Spy <chrispiropoulou@hotmail.com>\n"
"Language-Team: Greek <https://translate.pretix.eu/projects/pretix/pretix-js/" "Language-Team: Greek <https://translate.pretix.eu/projects/pretix/pretix-js/"
@@ -593,63 +593,55 @@ msgctxt "entry_status"
msgid "absent" msgid "absent"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:171 #: pretix/static/pretixcontrol/js/ui/editor.js:72
msgid "Check-in QR" msgid "Check-in QR"
msgstr "Έλεγχος QR" msgstr "Έλεγχος QR"
#: pretix/static/pretixcontrol/js/ui/editor.js:543 #: pretix/static/pretixcontrol/js/ui/editor.js:387
msgid "The PDF background file could not be loaded for the following reason:" msgid "The PDF background file could not be loaded for the following reason:"
msgstr "" msgstr ""
"Το αρχείο φόντου PDF δεν ήταν δυνατό να φορτωθεί για τον ακόλουθο λόγο:" "Το αρχείο φόντου PDF δεν ήταν δυνατό να φορτωθεί για τον ακόλουθο λόγο:"
#: pretix/static/pretixcontrol/js/ui/editor.js:894 #: pretix/static/pretixcontrol/js/ui/editor.js:656
msgid "Group of objects" msgid "Group of objects"
msgstr "Ομάδα αντικειμένων" msgstr "Ομάδα αντικειμένων"
#: pretix/static/pretixcontrol/js/ui/editor.js:899 #: pretix/static/pretixcontrol/js/ui/editor.js:661
#, fuzzy msgid "Text object"
#| msgid "Text object"
msgid "Text object (deprecated)"
msgstr "Αντικείμενο κειμένου" msgstr "Αντικείμενο κειμένου"
#: pretix/static/pretixcontrol/js/ui/editor.js:901 #: pretix/static/pretixcontrol/js/ui/editor.js:663
#, fuzzy
#| msgid "Text object"
msgid "Text box"
msgstr "Αντικείμενο κειμένου"
#: pretix/static/pretixcontrol/js/ui/editor.js:903
msgid "Barcode area" msgid "Barcode area"
msgstr "Περιοχή Barcode" msgstr "Περιοχή Barcode"
#: pretix/static/pretixcontrol/js/ui/editor.js:905 #: pretix/static/pretixcontrol/js/ui/editor.js:665
#, fuzzy #, fuzzy
#| msgid "Barcode area" #| msgid "Barcode area"
msgid "Image area" msgid "Image area"
msgstr "Περιοχή Barcode" msgstr "Περιοχή Barcode"
#: pretix/static/pretixcontrol/js/ui/editor.js:907 #: pretix/static/pretixcontrol/js/ui/editor.js:667
msgid "Powered by pretix" msgid "Powered by pretix"
msgstr "Υποστηρίζεται από το Pretix" msgstr "Υποστηρίζεται από το Pretix"
#: pretix/static/pretixcontrol/js/ui/editor.js:909 #: pretix/static/pretixcontrol/js/ui/editor.js:669
msgid "Object" msgid "Object"
msgstr "Αντικείμενο" msgstr "Αντικείμενο"
#: pretix/static/pretixcontrol/js/ui/editor.js:913 #: pretix/static/pretixcontrol/js/ui/editor.js:673
msgid "Ticket design" msgid "Ticket design"
msgstr "Σχεδιασμός εισιτηρίων" msgstr "Σχεδιασμός εισιτηρίων"
#: pretix/static/pretixcontrol/js/ui/editor.js:1250 #: pretix/static/pretixcontrol/js/ui/editor.js:972
msgid "Saving failed." msgid "Saving failed."
msgstr "Η αποθήκευση απέτυχε." msgstr "Η αποθήκευση απέτυχε."
#: pretix/static/pretixcontrol/js/ui/editor.js:1319 #: pretix/static/pretixcontrol/js/ui/editor.js:1041
#: pretix/static/pretixcontrol/js/ui/editor.js:1370 #: pretix/static/pretixcontrol/js/ui/editor.js:1091
msgid "Error while uploading your PDF file, please try again." msgid "Error while uploading your PDF file, please try again."
msgstr "Σφάλμα κατά τη μεταφόρτωση του αρχείου PDF, δοκιμάστε ξανά." msgstr "Σφάλμα κατά τη μεταφόρτωση του αρχείου PDF, δοκιμάστε ξανά."
#: pretix/static/pretixcontrol/js/ui/editor.js:1353 #: pretix/static/pretixcontrol/js/ui/editor.js:1074
msgid "Do you really want to leave the editor without saving your changes?" msgid "Do you really want to leave the editor without saving your changes?"
msgstr "" msgstr ""
"Θέλετε πραγματικά να αφήσετε τον επεξεργαστή χωρίς να αποθηκεύσετε τις " "Θέλετε πραγματικά να αφήσετε τον επεξεργαστή χωρίς να αποθηκεύσετε τις "
@@ -667,18 +659,18 @@ msgstr "Δημιουργία μηνυμάτων …"
msgid "Unknown error." msgid "Unknown error."
msgstr "Άγνωστο σφάλμα." msgstr "Άγνωστο σφάλμα."
#: pretix/static/pretixcontrol/js/ui/main.js:318 #: pretix/static/pretixcontrol/js/ui/main.js:313
msgid "Your color has great contrast and is very easy to read!" msgid "Your color has great contrast and is very easy to read!"
msgstr "" msgstr ""
"Το χρώμα σας έχει μεγάλη αντίθεση και είναι πολύ εύκολο να το διαβάσετε!" "Το χρώμα σας έχει μεγάλη αντίθεση και είναι πολύ εύκολο να το διαβάσετε!"
#: pretix/static/pretixcontrol/js/ui/main.js:322 #: pretix/static/pretixcontrol/js/ui/main.js:317
msgid "Your color has decent contrast and is probably good-enough to read!" msgid "Your color has decent contrast and is probably good-enough to read!"
msgstr "" msgstr ""
"Το χρώμα σας έχει αξιοπρεπή αντίθεση και είναι ίσως αρκετά καλό για να " "Το χρώμα σας έχει αξιοπρεπή αντίθεση και είναι ίσως αρκετά καλό για να "
"διαβάσετε!" "διαβάσετε!"
#: pretix/static/pretixcontrol/js/ui/main.js:326 #: pretix/static/pretixcontrol/js/ui/main.js:321
msgid "" msgid ""
"Your color has bad contrast for text on white background, please choose a " "Your color has bad contrast for text on white background, please choose a "
"darker shade." "darker shade."
@@ -686,40 +678,40 @@ msgstr ""
"Το χρώμα σας έχει κακή αντίθεση για κείμενο σε λευκό φόντο, επιλέξτε μια πιο " "Το χρώμα σας έχει κακή αντίθεση για κείμενο σε λευκό φόντο, επιλέξτε μια πιο "
"σκούρα σκιά." "σκούρα σκιά."
#: pretix/static/pretixcontrol/js/ui/main.js:491 #: pretix/static/pretixcontrol/js/ui/main.js:475
#: pretix/static/pretixcontrol/js/ui/main.js:511 #: pretix/static/pretixcontrol/js/ui/main.js:495
msgid "Search query" msgid "Search query"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:509 #: pretix/static/pretixcontrol/js/ui/main.js:493
msgid "All" msgid "All"
msgstr "Όλα" msgstr "Όλα"
#: pretix/static/pretixcontrol/js/ui/main.js:510 #: pretix/static/pretixcontrol/js/ui/main.js:494
msgid "None" msgid "None"
msgstr "Κανένας" msgstr "Κανένας"
#: pretix/static/pretixcontrol/js/ui/main.js:514 #: pretix/static/pretixcontrol/js/ui/main.js:498
msgid "Selected only" msgid "Selected only"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:847 #: pretix/static/pretixcontrol/js/ui/main.js:828
msgid "Enter page number between 1 and %(max)s." msgid "Enter page number between 1 and %(max)s."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:850 #: pretix/static/pretixcontrol/js/ui/main.js:831
msgid "Invalid page number." msgid "Invalid page number."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1008 #: pretix/static/pretixcontrol/js/ui/main.js:989
msgid "Use a different name internally" msgid "Use a different name internally"
msgstr "Χρησιμοποιήστε διαφορετικό όνομα εσωτερικά" msgstr "Χρησιμοποιήστε διαφορετικό όνομα εσωτερικά"
#: pretix/static/pretixcontrol/js/ui/main.js:1048 #: pretix/static/pretixcontrol/js/ui/main.js:1029
msgid "Click to close" msgid "Click to close"
msgstr "Κάντε κλικ για να κλείσετε" msgstr "Κάντε κλικ για να κλείσετε"
#: pretix/static/pretixcontrol/js/ui/main.js:1123 #: pretix/static/pretixcontrol/js/ui/main.js:1104
msgid "You have unsaved changes!" msgid "You have unsaved changes!"
msgstr "" msgstr ""
@@ -781,22 +773,22 @@ msgstr "απο %(currency)s %(price)s"
msgid "Please enter the amount the organizer can keep." msgid "Please enter the amount the organizer can keep."
msgstr "" msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:449 #: pretix/static/pretixpresale/js/ui/main.js:444
msgid "Please enter a quantity for one of the ticket types." msgid "Please enter a quantity for one of the ticket types."
msgstr "Εισαγάγετε μια ποσότητα για έναν από τους τύπους εισιτηρίων." msgstr "Εισαγάγετε μια ποσότητα για έναν από τους τύπους εισιτηρίων."
#: pretix/static/pretixpresale/js/ui/main.js:485 #: pretix/static/pretixpresale/js/ui/main.js:480
#, fuzzy #, fuzzy
#| msgid "Cart expired" #| msgid "Cart expired"
msgid "required" msgid "required"
msgstr "Το καλάθι έληξε" msgstr "Το καλάθι έληξε"
#: pretix/static/pretixpresale/js/ui/main.js:588 #: pretix/static/pretixpresale/js/ui/main.js:583
#: pretix/static/pretixpresale/js/ui/main.js:607 #: pretix/static/pretixpresale/js/ui/main.js:602
msgid "Time zone:" msgid "Time zone:"
msgstr "" msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:598 #: pretix/static/pretixpresale/js/ui/main.js:593
msgid "Your local time:" msgid "Your local time:"
msgstr "" msgstr ""
File diff suppressed because it is too large Load Diff
+33 -37
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-08-27 13:34+0000\n" "POT-Creation-Date: 2024-06-24 08:20+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -543,56 +543,52 @@ msgctxt "entry_status"
msgid "absent" msgid "absent"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:171 #: pretix/static/pretixcontrol/js/ui/editor.js:72
msgid "Check-in QR" msgid "Check-in QR"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:543 #: pretix/static/pretixcontrol/js/ui/editor.js:387
msgid "The PDF background file could not be loaded for the following reason:" msgid "The PDF background file could not be loaded for the following reason:"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:894 #: pretix/static/pretixcontrol/js/ui/editor.js:656
msgid "Group of objects" msgid "Group of objects"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:899 #: pretix/static/pretixcontrol/js/ui/editor.js:661
msgid "Text object (deprecated)" msgid "Text object"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:901 #: pretix/static/pretixcontrol/js/ui/editor.js:663
msgid "Text box"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:903
msgid "Barcode area" msgid "Barcode area"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:905 #: pretix/static/pretixcontrol/js/ui/editor.js:665
msgid "Image area" msgid "Image area"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:907 #: pretix/static/pretixcontrol/js/ui/editor.js:667
msgid "Powered by pretix" msgid "Powered by pretix"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:909 #: pretix/static/pretixcontrol/js/ui/editor.js:669
msgid "Object" msgid "Object"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:913 #: pretix/static/pretixcontrol/js/ui/editor.js:673
msgid "Ticket design" msgid "Ticket design"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:1250 #: pretix/static/pretixcontrol/js/ui/editor.js:972
msgid "Saving failed." msgid "Saving failed."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:1319 #: pretix/static/pretixcontrol/js/ui/editor.js:1041
#: pretix/static/pretixcontrol/js/ui/editor.js:1370 #: pretix/static/pretixcontrol/js/ui/editor.js:1091
msgid "Error while uploading your PDF file, please try again." msgid "Error while uploading your PDF file, please try again."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:1353 #: pretix/static/pretixcontrol/js/ui/editor.js:1074
msgid "Do you really want to leave the editor without saving your changes?" msgid "Do you really want to leave the editor without saving your changes?"
msgstr "" msgstr ""
@@ -608,54 +604,54 @@ msgstr ""
msgid "Unknown error." msgid "Unknown error."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:318 #: pretix/static/pretixcontrol/js/ui/main.js:313
msgid "Your color has great contrast and is very easy to read!" msgid "Your color has great contrast and is very easy to read!"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:322 #: pretix/static/pretixcontrol/js/ui/main.js:317
msgid "Your color has decent contrast and is probably good-enough to read!" msgid "Your color has decent contrast and is probably good-enough to read!"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:326 #: pretix/static/pretixcontrol/js/ui/main.js:321
msgid "" msgid ""
"Your color has bad contrast for text on white background, please choose a " "Your color has bad contrast for text on white background, please choose a "
"darker shade." "darker shade."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:491 #: pretix/static/pretixcontrol/js/ui/main.js:475
#: pretix/static/pretixcontrol/js/ui/main.js:511 #: pretix/static/pretixcontrol/js/ui/main.js:495
msgid "Search query" msgid "Search query"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:509 #: pretix/static/pretixcontrol/js/ui/main.js:493
msgid "All" msgid "All"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:510 #: pretix/static/pretixcontrol/js/ui/main.js:494
msgid "None" msgid "None"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:514 #: pretix/static/pretixcontrol/js/ui/main.js:498
msgid "Selected only" msgid "Selected only"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:847 #: pretix/static/pretixcontrol/js/ui/main.js:828
msgid "Enter page number between 1 and %(max)s." msgid "Enter page number between 1 and %(max)s."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:850 #: pretix/static/pretixcontrol/js/ui/main.js:831
msgid "Invalid page number." msgid "Invalid page number."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1008 #: pretix/static/pretixcontrol/js/ui/main.js:989
msgid "Use a different name internally" msgid "Use a different name internally"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1048 #: pretix/static/pretixcontrol/js/ui/main.js:1029
msgid "Click to close" msgid "Click to close"
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1123 #: pretix/static/pretixcontrol/js/ui/main.js:1104
msgid "You have unsaved changes!" msgid "You have unsaved changes!"
msgstr "" msgstr ""
@@ -705,20 +701,20 @@ msgstr ""
msgid "Please enter the amount the organizer can keep." msgid "Please enter the amount the organizer can keep."
msgstr "" msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:449 #: pretix/static/pretixpresale/js/ui/main.js:444
msgid "Please enter a quantity for one of the ticket types." msgid "Please enter a quantity for one of the ticket types."
msgstr "" msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:485 #: pretix/static/pretixpresale/js/ui/main.js:480
msgid "required" msgid "required"
msgstr "" msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:588 #: pretix/static/pretixpresale/js/ui/main.js:583
#: pretix/static/pretixpresale/js/ui/main.js:607 #: pretix/static/pretixpresale/js/ui/main.js:602
msgid "Time zone:" msgid "Time zone:"
msgstr "" msgstr ""
#: pretix/static/pretixpresale/js/ui/main.js:598 #: pretix/static/pretixpresale/js/ui/main.js:593
msgid "Your local time:" msgid "Your local time:"
msgstr "" msgstr ""
File diff suppressed because it is too large Load Diff
+46 -54
View File
@@ -7,17 +7,17 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-08-27 13:34+0000\n" "POT-Creation-Date: 2024-06-24 08:20+0000\n"
"PO-Revision-Date: 2024-07-19 08:56+0000\n" "PO-Revision-Date: 2024-06-25 01:00+0000\n"
"Last-Translator: Reece Needham <nouveaureece@protonmail.com>\n" "Last-Translator: Reece Needham <nouveaureece@protonmail.com>\n"
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/pretix-" "Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/"
"js/es/>\n" "pretix-js/es/>\n"
"Language: es\n" "Language: es\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n" "Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.6.2\n" "X-Generator: Weblate 5.5.5\n"
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56 #: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62 #: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
@@ -254,7 +254,7 @@ msgstr "Cancelar"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:51 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:51
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:60 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:60
msgid "Ticket not paid" msgid "Ticket not paid"
msgstr "Entrada no pagada" msgstr "Ticket por pagar"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:52 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:52
msgid "This ticket is not yet paid. Do you want to continue anyways?" msgid "This ticket is not yet paid. Do you want to continue anyways?"
@@ -274,7 +274,7 @@ msgstr "Salida registrada"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:56 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:56
msgid "Ticket already used" msgid "Ticket already used"
msgstr "Esta entrada ya fue utilizada" msgstr "Este ticket ya fue utilizado"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:57 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:57
msgid "Information required" msgid "Information required"
@@ -282,11 +282,11 @@ msgstr "Información requerida"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:58 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:58
msgid "Unknown ticket" msgid "Unknown ticket"
msgstr "Entrada desconocida" msgstr "Ticket no encontrado"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:59 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:59
msgid "Ticket type not allowed here" msgid "Ticket type not allowed here"
msgstr "Tipo de entrada no está permitido" msgstr "Tipo de ticket no está permitido"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:61 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:61
msgid "Entry not allowed" msgid "Entry not allowed"
@@ -294,19 +294,19 @@ msgstr "Entrada no permitida"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:62 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:62
msgid "Ticket code revoked/changed" msgid "Ticket code revoked/changed"
msgstr "Código de entrada revocada/cambiada" msgstr "Código de ticket revocado/cambiado"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:63 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:63
msgid "Ticket blocked" msgid "Ticket blocked"
msgstr "Entrada bloqueada" msgstr "Ticket bloqueado"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:64 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:64
msgid "Ticket not valid at this time" msgid "Ticket not valid at this time"
msgstr "Entrada no válida en este momento" msgstr "Ticket no válido en este momento"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:65 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:65
msgid "Order canceled" msgid "Order canceled"
msgstr "Pedido cancelado" msgstr "Orden cancelada"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:66 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:66
msgid "Ticket code is ambiguous on list" msgid "Ticket code is ambiguous on list"
@@ -556,63 +556,55 @@ msgctxt "entry_status"
msgid "absent" msgid "absent"
msgstr "ausente" msgstr "ausente"
#: pretix/static/pretixcontrol/js/ui/editor.js:171 #: pretix/static/pretixcontrol/js/ui/editor.js:72
msgid "Check-in QR" msgid "Check-in QR"
msgstr "QR de Chequeo" msgstr "QR de Chequeo"
#: pretix/static/pretixcontrol/js/ui/editor.js:543 #: pretix/static/pretixcontrol/js/ui/editor.js:387
msgid "The PDF background file could not be loaded for the following reason:" msgid "The PDF background file could not be loaded for the following reason:"
msgstr "" msgstr ""
"El archivo PDF de fondo no ha podido ser cargado debido al siguiente motivo:" "El archivo PDF de fondo no ha podido ser cargado debido al siguiente motivo:"
#: pretix/static/pretixcontrol/js/ui/editor.js:894 #: pretix/static/pretixcontrol/js/ui/editor.js:656
msgid "Group of objects" msgid "Group of objects"
msgstr "Grupo de objetos" msgstr "Grupo de objetos"
#: pretix/static/pretixcontrol/js/ui/editor.js:899 #: pretix/static/pretixcontrol/js/ui/editor.js:661
#, fuzzy msgid "Text object"
#| msgid "Text object"
msgid "Text object (deprecated)"
msgstr "Objeto de texto" msgstr "Objeto de texto"
#: pretix/static/pretixcontrol/js/ui/editor.js:901 #: pretix/static/pretixcontrol/js/ui/editor.js:663
#, fuzzy
#| msgid "Text object"
msgid "Text box"
msgstr "Objeto de texto"
#: pretix/static/pretixcontrol/js/ui/editor.js:903
msgid "Barcode area" msgid "Barcode area"
msgstr "Área para código de barras" msgstr "Área para código de barras"
#: pretix/static/pretixcontrol/js/ui/editor.js:905 #: pretix/static/pretixcontrol/js/ui/editor.js:665
msgid "Image area" msgid "Image area"
msgstr "Área de imagen" msgstr "Área de imagen"
#: pretix/static/pretixcontrol/js/ui/editor.js:907 #: pretix/static/pretixcontrol/js/ui/editor.js:667
msgid "Powered by pretix" msgid "Powered by pretix"
msgstr "Proveído por pretix" msgstr "Proveído por pretix"
#: pretix/static/pretixcontrol/js/ui/editor.js:909 #: pretix/static/pretixcontrol/js/ui/editor.js:669
msgid "Object" msgid "Object"
msgstr "Objeto" msgstr "Objeto"
#: pretix/static/pretixcontrol/js/ui/editor.js:913 #: pretix/static/pretixcontrol/js/ui/editor.js:673
msgid "Ticket design" msgid "Ticket design"
msgstr "Diseño del entrada" msgstr "Diseño del ticket"
#: pretix/static/pretixcontrol/js/ui/editor.js:1250 #: pretix/static/pretixcontrol/js/ui/editor.js:972
msgid "Saving failed." msgid "Saving failed."
msgstr "El guardado falló." msgstr "El guardado falló."
#: pretix/static/pretixcontrol/js/ui/editor.js:1319 #: pretix/static/pretixcontrol/js/ui/editor.js:1041
#: pretix/static/pretixcontrol/js/ui/editor.js:1370 #: pretix/static/pretixcontrol/js/ui/editor.js:1091
msgid "Error while uploading your PDF file, please try again." msgid "Error while uploading your PDF file, please try again."
msgstr "" msgstr ""
"Ha habido un error mientras se cargaba el archivo PDF, por favor, intente de " "Ha habido un error mientras se cargaba el archivo PDF, por favor, intente de "
"nuevo." "nuevo."
#: pretix/static/pretixcontrol/js/ui/editor.js:1353 #: pretix/static/pretixcontrol/js/ui/editor.js:1074
msgid "Do you really want to leave the editor without saving your changes?" msgid "Do you really want to leave the editor without saving your changes?"
msgstr "¿Realmente desea salir del editor sin haber guardado sus cambios?" msgstr "¿Realmente desea salir del editor sin haber guardado sus cambios?"
@@ -628,17 +620,17 @@ msgstr "Generando mensajes…"
msgid "Unknown error." msgid "Unknown error."
msgstr "Error desconocido." msgstr "Error desconocido."
#: pretix/static/pretixcontrol/js/ui/main.js:318 #: pretix/static/pretixcontrol/js/ui/main.js:313
msgid "Your color has great contrast and is very easy to read!" msgid "Your color has great contrast and is very easy to read!"
msgstr "¡Tu color tiene gran contraste y es muy fácil de leer!" msgstr "¡Tu color tiene gran contraste y es muy fácil de leer!"
#: pretix/static/pretixcontrol/js/ui/main.js:322 #: pretix/static/pretixcontrol/js/ui/main.js:317
msgid "Your color has decent contrast and is probably good-enough to read!" msgid "Your color has decent contrast and is probably good-enough to read!"
msgstr "" msgstr ""
"¡Tu color tiene un contraste decente y es probablemente lo suficientemente " "¡Tu color tiene un contraste decente y es probablemente lo suficientemente "
"legible!" "legible!"
#: pretix/static/pretixcontrol/js/ui/main.js:326 #: pretix/static/pretixcontrol/js/ui/main.js:321
msgid "" msgid ""
"Your color has bad contrast for text on white background, please choose a " "Your color has bad contrast for text on white background, please choose a "
"darker shade." "darker shade."
@@ -646,40 +638,40 @@ msgstr ""
"Tu color tiene mal contraste para un texto con fondo blanco, por favor, " "Tu color tiene mal contraste para un texto con fondo blanco, por favor, "
"escoge un tono más oscuro." "escoge un tono más oscuro."
#: pretix/static/pretixcontrol/js/ui/main.js:491 #: pretix/static/pretixcontrol/js/ui/main.js:475
#: pretix/static/pretixcontrol/js/ui/main.js:511 #: pretix/static/pretixcontrol/js/ui/main.js:495
msgid "Search query" msgid "Search query"
msgstr "Consultar búsqueda" msgstr "Consultar búsqueda"
#: pretix/static/pretixcontrol/js/ui/main.js:509 #: pretix/static/pretixcontrol/js/ui/main.js:493
msgid "All" msgid "All"
msgstr "Todos" msgstr "Todos"
#: pretix/static/pretixcontrol/js/ui/main.js:510 #: pretix/static/pretixcontrol/js/ui/main.js:494
msgid "None" msgid "None"
msgstr "Ninguno" msgstr "Ninguno"
#: pretix/static/pretixcontrol/js/ui/main.js:514 #: pretix/static/pretixcontrol/js/ui/main.js:498
msgid "Selected only" msgid "Selected only"
msgstr "Solamente seleccionados" msgstr "Solamente seleccionados"
#: pretix/static/pretixcontrol/js/ui/main.js:847 #: pretix/static/pretixcontrol/js/ui/main.js:828
msgid "Enter page number between 1 and %(max)s." msgid "Enter page number between 1 and %(max)s."
msgstr "Introduce un número de página entre 1 y %(max)s." msgstr "Introduce un número de página entre 1 y %(max)s."
#: pretix/static/pretixcontrol/js/ui/main.js:850 #: pretix/static/pretixcontrol/js/ui/main.js:831
msgid "Invalid page number." msgid "Invalid page number."
msgstr "Número de página inválido." msgstr "Número de página inválido."
#: pretix/static/pretixcontrol/js/ui/main.js:1008 #: pretix/static/pretixcontrol/js/ui/main.js:989
msgid "Use a different name internally" msgid "Use a different name internally"
msgstr "Usar un nombre diferente internamente" msgstr "Usar un nombre diferente internamente"
#: pretix/static/pretixcontrol/js/ui/main.js:1048 #: pretix/static/pretixcontrol/js/ui/main.js:1029
msgid "Click to close" msgid "Click to close"
msgstr "Click para cerrar" msgstr "Click para cerrar"
#: pretix/static/pretixcontrol/js/ui/main.js:1123 #: pretix/static/pretixcontrol/js/ui/main.js:1104
msgid "You have unsaved changes!" msgid "You have unsaved changes!"
msgstr "¡Tienes cambios sin guardar!" msgstr "¡Tienes cambios sin guardar!"
@@ -734,20 +726,20 @@ msgstr "Obtienes %(currency)s %(price)s de vuelta"
msgid "Please enter the amount the organizer can keep." msgid "Please enter the amount the organizer can keep."
msgstr "Por favor, ingrese el monto que el organizador puede quedarse." msgstr "Por favor, ingrese el monto que el organizador puede quedarse."
#: pretix/static/pretixpresale/js/ui/main.js:449 #: pretix/static/pretixpresale/js/ui/main.js:444
msgid "Please enter a quantity for one of the ticket types." msgid "Please enter a quantity for one of the ticket types."
msgstr "Por favor, introduzca un valor para cada tipo de entrada." msgstr "Por favor, introduzca un valor para cada tipo de entrada."
#: pretix/static/pretixpresale/js/ui/main.js:485 #: pretix/static/pretixpresale/js/ui/main.js:480
msgid "required" msgid "required"
msgstr "campo requerido" msgstr "campo requerido"
#: pretix/static/pretixpresale/js/ui/main.js:588 #: pretix/static/pretixpresale/js/ui/main.js:583
#: pretix/static/pretixpresale/js/ui/main.js:607 #: pretix/static/pretixpresale/js/ui/main.js:602
msgid "Time zone:" msgid "Time zone:"
msgstr "Zona horaria:" msgstr "Zona horaria:"
#: pretix/static/pretixpresale/js/ui/main.js:598 #: pretix/static/pretixpresale/js/ui/main.js:593
msgid "Your local time:" msgid "Your local time:"
msgstr "Su hora local:" msgstr "Su hora local:"

Some files were not shown because too many files have changed in this diff Show More