Compare commits

..

20 Commits

Author SHA1 Message Date
Richard Schreiber
5aa8b6c329 Close dialog onclick on backdrop 2025-05-21 20:47:17 +02:00
luelista
a84beef269 [A11y] Cart renewal (#5109)
Allow customers to extend their cart reservation up to 11 times the configured reservation time
2025-05-21 17:02:21 +02:00
Richard Schreiber
7c59ec51ca [A11y] switch modal dialogs to <dialog> tag (#5128)
* add templatetag {% dialog %} using <dialog> tag
* new dialog style
* show dialog when empty add-to-cart instead of disabling the button
* update cookieconsent-modal to use new template tag
2025-05-21 16:52:49 +02:00
Richard Schreiber
bf47da521c [A11y] add heading-level to product list (#5121) 2025-05-21 13:31:05 +02:00
Richard Schreiber
e3b74249c9 [A11y] fix skip-link z-index (#5123) 2025-05-21 13:30:39 +02:00
Richard Schreiber
1791a63f87 [A11y] Do not underline event-titles in organizer’s event-list (#5124) 2025-05-21 13:26:27 +02:00
Raphael Michel
f931362bc5 Add missing flag for Basque language 2025-05-21 12:57:19 +02:00
Raphael Michel
a836dc1588 Add Hebrew language 2025-05-21 12:52:53 +02:00
Raphael Michel
8b6685dd89 Translations: Update Hebrew
Currently translated at 100.0% (5885 of 5885 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/he/

powered by weblate
2025-05-21 12:47:42 +02:00
roi belotsercovsky
7463e41be8 Translations: Update Hebrew
Currently translated at 0.0% (0 of 5869 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/he/

powered by weblate
2025-05-21 12:47:42 +02:00
Raphael Michel
31b2a9026d Translations: Add Hebrew 2025-05-21 12:47:42 +02:00
Raphael Michel
1d49d7cbf7 Translations: Delete Hebrew 2025-05-21 12:47:42 +02:00
조정화
30570fe287 Translations: Update Korean
Currently translated at 39.5% (2322 of 5869 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/ko/

powered by weblate
2025-05-21 12:47:42 +02:00
roi belotsercovsky
00508dea99 Translations: Update Hebrew
Currently translated at 0.1% (1 of 5869 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/he/

powered by weblate
2025-05-21 12:47:42 +02:00
Patrick Chilton
6be4e2bd7b Translations: Update Hungarian
Currently translated at 10.9% (642 of 5869 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/hu/

powered by weblate
2025-05-21 12:47:42 +02:00
bstramsek
b014446399 Translations: Update Slovenian
Currently translated at 39.3% (96 of 244 strings)

Translation: pretix/pretix (JavaScript parts)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/sl/

powered by weblate
2025-05-21 12:47:42 +02:00
David
5053d4db6b Translations: Update Czech
Currently translated at 96.3% (235 of 244 strings)

Translation: pretix/pretix (JavaScript parts)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/cs/

powered by weblate
2025-05-21 12:47:42 +02:00
David
ae2cc7a04a Translations: Update Czech
Currently translated at 73.6% (4322 of 5869 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/cs/

powered by weblate
2025-05-21 12:47:42 +02:00
Tobias Kunze
d49141c05d Use he as language code for Hebrew (#5129) 2025-05-21 12:42:51 +02:00
Richard Schreiber
0bbb136d67 [A11y] Fix checkbox:focus style (#5131) 2025-05-21 12:00:34 +02:00
30 changed files with 10044 additions and 12425 deletions

View File

@@ -101,6 +101,7 @@ ALL_LANGUAGES = [
('fi', _('Finnish')),
('gl', _('Galician')),
('el', _('Greek')),
('he', _('Hebrew')),
('id', _('Indonesian')),
('it', _('Italian')),
('ja', _('Japanese')),
@@ -122,7 +123,7 @@ LANGUAGES_OFFICIAL = {
}
LANGUAGES_RTL = {
# When adding more right-to-left languages, also update pretix/static/pretixbase/scss/_rtl.scss
'ar', 'hw'
'ar', 'he'
}
LANGUAGES_INCUBATING = {
'pt-br', 'gl',

View File

@@ -0,0 +1,18 @@
# Generated by Django 4.2.20 on 2025-05-14 14:58
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0279_discount_event_date_from_discount_event_date_until'),
]
operations = [
migrations.AddField(
model_name='cartposition',
name='max_extend',
field=models.DateTimeField(null=True),
),
]

View File

@@ -3098,7 +3098,10 @@ class CartPosition(AbstractPosition):
verbose_name=_("Expiration date"),
db_index=True
)
max_extend = models.DateTimeField(
verbose_name=_("Limit for extending expiration date"),
null=True
)
tax_rate = models.DecimalField(
max_digits=7, decimal_places=2, default=Decimal('0.00'),
verbose_name=_('Tax rate')

View File

@@ -45,6 +45,7 @@ from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import DatabaseError, transaction
from django.db.models import Count, Exists, IntegerField, OuterRef, Q, Value
from django.db.models.aggregates import Min
from django.dispatch import receiver
from django.utils.timezone import make_aware, now
from django.utils.translation import (
@@ -275,7 +276,10 @@ class CartManager:
}
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, reservation_time: timedelta=None):
"""
Creates a new CartManager for an event.
"""
self.event = event
self.cart_id = cart_id
self.real_now_dt = now()
@@ -286,11 +290,17 @@ class CartManager:
self._subevents_cache = {}
self._variations_cache = {}
self._seated_cache = {}
self._expiry = None
self._explicit_expiry = expiry
self.invoice_address = invoice_address
self._widget_data = widget_data or {}
self._sales_channel = sales_channel
self.num_extended_positions = 0
if reservation_time:
self._reservation_time = reservation_time
else:
self._reservation_time = timedelta(minutes=self.event.settings.get('reservation_time', as_type=int))
self._expiry = self.real_now_dt + self._reservation_time
self._max_expiry_extend = self.real_now_dt + (self._reservation_time * 11)
@property
def positions(self):
@@ -305,14 +315,6 @@ class CartManager:
self._seated_cache[item, subevent] = item.seat_category_mappings.filter(subevent=subevent).exists()
return self._seated_cache[item, subevent]
def _calculate_expiry(self):
if self._explicit_expiry:
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):
if self.event.presale_start and time_machine_now(self.real_now_dt) < self.event.presale_start:
raise CartError(error_messages['not_started'])
@@ -329,9 +331,27 @@ class CartManager:
raise CartError(error_messages['payment_ended'])
def _extend_expiry_of_valid_existing_positions(self):
# real_now_dt is initialized at CartManager instantiation, so it's slightly in the past. Add a small
# delta to reduce risk of extending already expired CartPositions.
padded_now_dt = self.real_now_dt + timedelta(seconds=5)
# Make sure we do not extend past the max_extend timestamp, allowing users to extend their valid positions up
# to 11 times the reservation time. If we add new positions to the cart while valid positions exist, the new
# positions' reservation will also be limited to max_extend of the oldest position.
# Only after all positions expire, an ExtendOperation may reset max_extend to another 11x reservation_time.
max_extend_existing = self.positions.filter(expires__gt=padded_now_dt).aggregate(m=Min('max_extend'))['m']
if max_extend_existing:
self._expiry = min(self._expiry, max_extend_existing)
self._max_expiry_extend = max_extend_existing
# Extend this user's cart session to ensure all items in the cart expire at the same time
# We can extend the reservation of items which are not yet expired without risk
self.positions.filter(expires__gt=self.real_now_dt).update(expires=self._expiry)
if self._expiry > padded_now_dt:
self.num_extended_positions += self.positions.filter(
expires__gt=padded_now_dt, expires__lt=self._expiry,
).update(
expires=self._expiry,
)
def _delete_out_of_timeframe(self):
err = None
@@ -1246,6 +1266,7 @@ class CartManager:
item=op.item,
variation=op.variation,
expires=self._expiry,
max_extend=self._max_expiry_extend,
cart_id=self.cart_id,
voucher=op.voucher,
addon_to=op.addon_to if op.addon_to else None,
@@ -1294,7 +1315,9 @@ class CartManager:
event=self.event,
item=b.item,
variation=b.variation,
expires=self._expiry, cart_id=self.cart_id,
expires=self._expiry,
max_extend=self._max_expiry_extend,
cart_id=self.cart_id,
voucher=None,
addon_to=cp,
subevent=b.subevent,
@@ -1321,12 +1344,14 @@ class CartManager:
op.position.delete()
elif available_count == 1:
op.position.expires = self._expiry
op.position.max_extend = self._max_expiry_extend
op.position.listed_price = op.listed_price
op.position.price_after_voucher = op.price_after_voucher
# op.position.price will be updated by recompute_final_prices_and_taxes()
if op.position.pk not in deleted_positions:
try:
op.position.save(force_update=True, update_fields=['expires', 'listed_price', 'price_after_voucher'])
op.position.save(force_update=True, update_fields=['expires', 'max_extend', 'listed_price', 'price_after_voucher'])
self.num_extended_positions += 1
except DatabaseError:
# Best effort... The position might have been deleted in the meantime!
pass
@@ -1416,14 +1441,11 @@ class CartManager:
def commit(self):
self._check_presale_dates()
self._check_max_cart_size()
self._calculate_expiry()
err = self._delete_out_of_timeframe()
err = self.extend_expired_positions() or err
err = err or self._check_min_per_voucher()
self.real_now_dt = now()
self._extend_expiry_of_valid_existing_positions()
err = self._perform_operations() or err
self.recompute_final_prices_and_taxes()
@@ -1632,6 +1654,31 @@ def clear_cart(self, event: Event, cart_id: str=None, locale='en', sales_channel
raise CartError(error_messages['busy'])
@app.task(base=ProfiledEventTask, bind=True, max_retries=5, default_retry_delay=1, throws=(CartError,))
def extend_cart_reservation(self, event: Event, cart_id: str=None, locale='en', sales_channel='web', override_now_dt: datetime=None) -> None:
"""
Resets the expiry time of a cart to the configured reservation time of this event.
Limited to 11x the reservation time.
:param event: The event ID in question
:param cart_id: The cart ID of the cart to modify
"""
with language(locale), time_machine_now_assigned(override_now_dt):
try:
sales_channel = event.organizer.sales_channels.get(identifier=sales_channel)
except SalesChannel.DoesNotExist:
raise CartError("Invalid sales channel.")
try:
try:
cm = CartManager(event=event, cart_id=cart_id, sales_channel=sales_channel)
cm.commit()
return cm.num_extended_positions
except LockTimeoutException:
self.retry()
except (MaxRetriesExceededError, LockTimeoutException):
raise CartError(error_messages['busy'])
@app.task(base=ProfiledEventTask, bind=True, max_retries=5, default_retry_delay=1, throws=(CartError,))
def set_cart_addons(self, event: Event, addons: List[dict], add_to_cart_items: List[dict], cart_id: str=None, locale='en',
invoice_address: int=None, sales_channel='web', override_now_dt: datetime=None) -> None:

View File

@@ -0,0 +1,60 @@
#
# 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 template
from django.utils.html import format_html
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _ # NOQA
register = template.Library()
@register.simple_tag
def dialog(html_id, label, description, *args, **kwargs):
format_kwargs = {
"id": html_id,
"label": label,
"description": description,
"icon": format_html('<div class="modal-card-icon"><span class="fa fa-{}" aria-hidden="true"></span></div>', kwargs["icon"]) if "icon" in kwargs else "",
"alert": mark_safe('role="alertdialog"') if kwargs.get("alert", "False") != "False" else "",
}
result = """
<dialog {alert}
id="{id}"
aria-labelledby="{id}-label"
aria-describedby="{id}-description">
<form method="dialog" class="modal-card form-horizontal">
{icon}
<div class="modal-card-content">
<h2 id="{id}-label">{label}</h2>
<p id="{id}-description">{description}</p>
"""
return format_html(result, **format_kwargs)
@register.simple_tag
def enddialog(*args, **kwargs):
return mark_safe("""
</div>
</form>
</dialog>
""")

View File

@@ -8,16 +8,16 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-04-28 11:06+0000\n"
"PO-Revision-Date: 2025-02-19 17:00+0000\n"
"Last-Translator: Petr Čermák <pcermak@live.com>\n"
"Language-Team: Czech <https://translate.pretix.eu/projects/pretix/pretix/cs/"
">\n"
"PO-Revision-Date: 2025-05-16 17:00+0000\n"
"Last-Translator: David <davemachala@gmail.com>\n"
"Language-Team: Czech <https://translate.pretix.eu/projects/pretix/pretix/cs/>"
"\n"
"Language: cs\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
"X-Generator: Weblate 5.10\n"
"X-Generator: Weblate 5.11.4\n"
#: pretix/_base_settings.py:87
msgid "English"
@@ -57,7 +57,7 @@ msgstr "Čeština"
#: pretix/_base_settings.py:96
msgid "Croatian"
msgstr ""
msgstr "Chorvatština"
#: pretix/_base_settings.py:97
msgid "Danish"
@@ -2971,12 +2971,9 @@ msgid "Repeat password"
msgstr "Opakovat heslo"
#: pretix/base/forms/questions.py:134 pretix/base/forms/questions.py:256
#, fuzzy
#| msgctxt "subevent"
#| msgid "No date was specified."
msgctxt "name_salutation"
msgid "not specified"
msgstr "Nebylo uvedeno žádné datum."
msgstr "neuvedeno"
#: pretix/base/forms/questions.py:219
msgid "Please do not use special characters in names."
@@ -4227,20 +4224,14 @@ msgstr ""
"automatická sleva poskytnuta i nadále."
#: pretix/base/models/discount.py:177
#, fuzzy
#| msgctxt "subevent"
#| msgid "All dates starting before"
msgctxt "subevent"
msgid "Available for dates starting from"
msgstr "Všechny termíny začínající před"
msgstr "Dostupné pro termíny začínající od"
#: pretix/base/models/discount.py:182
#, fuzzy
#| msgctxt "subevent"
#| msgid "All dates starting before"
msgctxt "subevent"
msgid "Available for dates starting until"
msgstr "Všechny termíny začínající před"
msgstr "Dostupné pro termíny začínající do"
#: pretix/base/models/discount.py:214
msgid ""
@@ -13474,7 +13465,7 @@ msgstr "Schváleno, čeká se na platbu"
#: pretix/plugins/reports/exporters.py:380
#: pretix/presale/templates/pretixpresale/event/fragment_order_status.html:7
msgid "Approval pending"
msgstr "Čeká se na schválení"
msgstr "Čeká na schválení"
#: pretix/control/forms/filter.py:241
#, fuzzy
@@ -18849,7 +18840,7 @@ msgstr "Kontrola"
#: pretix/control/templates/pretixcontrol/organizers/device_logs.html:50
#: pretix/control/templates/pretixcontrol/organizers/logs.html:80
msgid "No results"
msgstr "Bez výsledků"
msgstr "Žádné výsledky"
#: pretix/control/templates/pretixcontrol/event/mail.html:7
#: pretix/control/templates/pretixcontrol/organizers/mail.html:11

View File

@@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-04-28 11:08+0000\n"
"PO-Revision-Date: 2025-02-19 17:00+0000\n"
"Last-Translator: Petr Čermák <pcermak@live.com>\n"
"PO-Revision-Date: 2025-05-16 17:00+0000\n"
"Last-Translator: David <davemachala@gmail.com>\n"
"Language-Team: Czech <https://translate.pretix.eu/projects/pretix/pretix-js/"
"cs/>\n"
"Language: cs\n"
@@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
"X-Generator: Weblate 5.10\n"
"X-Generator: Weblate 5.11.4\n"
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
@@ -237,11 +237,11 @@ msgstr "Zrušeno"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:46
msgid "Confirmed"
msgstr ""
msgstr "Potvrzeno"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:47
msgid "Approval pending"
msgstr ""
msgstr "Čeká na schválení"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
msgid "Redeemed"
@@ -440,7 +440,7 @@ msgstr "je po"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:40
msgid "="
msgstr ""
msgstr "="
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:99
msgid "Product"
@@ -452,7 +452,7 @@ msgstr "Varianta produktu"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:107
msgid "Gate"
msgstr ""
msgstr "Brána"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:111
msgid "Current date and time"
@@ -557,12 +557,12 @@ msgstr "Duplikát"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:193
msgctxt "entry_status"
msgid "present"
msgstr ""
msgstr "přítomen"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:194
msgctxt "entry_status"
msgid "absent"
msgstr ""
msgstr "nepřítomen"
#: pretix/static/pretixcontrol/js/ui/editor.js:171
msgid "Check-in QR"
@@ -692,10 +692,8 @@ msgid "Calculating default price…"
msgstr "Výpočet standardní ceny…"
#: pretix/static/pretixcontrol/js/ui/plugins.js:69
#, fuzzy
#| msgid "Search results"
msgid "No results"
msgstr "Vyhledat výsledky"
msgstr "Žádné výsledky"
#: pretix/static/pretixcontrol/js/ui/question.js:42
msgid "Others"
@@ -764,64 +762,57 @@ msgid "Your local time:"
msgstr "Místní čas:"
#: pretix/static/pretixpresale/js/walletdetection.js:39
#, fuzzy
#| msgid "Apple Pay"
msgid "Google Pay"
msgstr "Apple Pay"
msgstr "Google Pay"
#: pretix/static/pretixpresale/js/widget/widget.js:16
msgctxt "widget"
msgid "Quantity"
msgstr ""
msgstr "Počet"
#: pretix/static/pretixpresale/js/widget/widget.js:17
msgctxt "widget"
msgid "Decrease quantity"
msgstr ""
msgstr "Snížit počet"
#: pretix/static/pretixpresale/js/widget/widget.js:18
msgctxt "widget"
msgid "Increase quantity"
msgstr ""
msgstr "Zvýšit počet"
#: pretix/static/pretixpresale/js/widget/widget.js:19
msgctxt "widget"
msgid "Price"
msgstr ""
msgstr "Cena"
#: pretix/static/pretixpresale/js/widget/widget.js:20
#, javascript-format
msgctxt "widget"
msgid "Original price: %s"
msgstr ""
msgstr "Původní cena: %s"
#: pretix/static/pretixpresale/js/widget/widget.js:21
#, javascript-format
msgctxt "widget"
msgid "New price: %s"
msgstr ""
msgstr "Nová cena: %s"
#: pretix/static/pretixpresale/js/widget/widget.js:22
#, fuzzy
#| msgid "Selected only"
msgctxt "widget"
msgid "Select"
msgstr "Pouze vybra"
msgstr "Vybrat"
#: pretix/static/pretixpresale/js/widget/widget.js:23
#, fuzzy, javascript-format
#| msgid "Selected only"
#, javascript-format
msgctxt "widget"
msgid "Select %s"
msgstr "Pouze vybrané"
msgstr "Vybrat %s"
#: pretix/static/pretixpresale/js/widget/widget.js:24
#, fuzzy, javascript-format
#| msgctxt "widget"
#| msgid "See variations"
#, javascript-format
msgctxt "widget"
msgid "Select variant %s"
msgstr "Zobrazit možnosti"
msgstr "Vybrat variantu %s"
#: pretix/static/pretixpresale/js/widget/widget.js:25
msgctxt "widget"
@@ -857,7 +848,7 @@ msgstr "od %(currency)s %(price)s"
#, javascript-format
msgctxt "widget"
msgid "Image of %s"
msgstr ""
msgstr "Obrázek%s"
#: pretix/static/pretixpresale/js/widget/widget.js:32
msgctxt "widget"
@@ -892,24 +883,19 @@ msgstr "K dispozici pouze s poukazem"
#: pretix/static/pretixpresale/js/widget/widget.js:38
#: pretix/static/pretixpresale/js/widget/widget.js:41
#, fuzzy
#| msgid "Payment method unavailable"
msgctxt "widget"
msgid "Not yet available"
msgstr "Způsob platby není k dispozici"
msgstr "Zatím není k dispozici"
#: pretix/static/pretixpresale/js/widget/widget.js:39
msgctxt "widget"
msgid "Not available anymore"
msgstr ""
msgstr "Již není k dispozici"
#: pretix/static/pretixpresale/js/widget/widget.js:40
#, fuzzy
#| msgctxt "widget"
#| msgid "currently available: %s"
msgctxt "widget"
msgid "Currently not available"
msgstr "aktuálně k dispozici: %s"
msgstr "Momentálně není k dispozici."
#: pretix/static/pretixpresale/js/widget/widget.js:42
#, javascript-format
@@ -942,12 +928,9 @@ msgid "Open ticket shop"
msgstr "Obchod vstupenek otevřit"
#: pretix/static/pretixpresale/js/widget/widget.js:48
#, fuzzy
#| msgctxt "widget"
#| msgid "Resume checkout"
msgctxt "widget"
msgid "Checkout"
msgstr "Obnovit checkout"
msgstr "Checkout"
#: pretix/static/pretixpresale/js/widget/widget.js:49
msgctxt "widget"
@@ -1009,20 +992,14 @@ msgid "Continue"
msgstr "Pokračovat"
#: pretix/static/pretixpresale/js/widget/widget.js:61
#, fuzzy
#| msgctxt "widget"
#| msgid "See variations"
msgctxt "widget"
msgid "Show variants"
msgstr "Zobrazit možnosti"
#: pretix/static/pretixpresale/js/widget/widget.js:62
#, fuzzy
#| msgctxt "widget"
#| msgid "See variations"
msgctxt "widget"
msgid "Hide variants"
msgstr "Zobrazit možnosti"
msgstr "Skrýt možnosti"
#: pretix/static/pretixpresale/js/widget/widget.js:63
msgctxt "widget"
@@ -1110,31 +1087,31 @@ msgstr "Ne"
#: pretix/static/pretixpresale/js/widget/widget.js:81
msgid "Monday"
msgstr ""
msgstr "Pondělí"
#: pretix/static/pretixpresale/js/widget/widget.js:82
msgid "Tuesday"
msgstr ""
msgstr "Úterý"
#: pretix/static/pretixpresale/js/widget/widget.js:83
msgid "Wednesday"
msgstr ""
msgstr "Středa"
#: pretix/static/pretixpresale/js/widget/widget.js:84
msgid "Thursday"
msgstr ""
msgstr "Čtvrtek"
#: pretix/static/pretixpresale/js/widget/widget.js:85
msgid "Friday"
msgstr ""
msgstr "Pátek"
#: pretix/static/pretixpresale/js/widget/widget.js:86
msgid "Saturday"
msgstr ""
msgstr "Sobota"
#: pretix/static/pretixpresale/js/widget/widget.js:87
msgid "Sunday"
msgstr ""
msgstr "Neděle"
#: pretix/static/pretixpresale/js/widget/widget.js:90
msgid "January"

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-04-28 11:06+0000\n"
"PO-Revision-Date: 2025-04-14 23:00+0000\n"
"PO-Revision-Date: 2025-05-17 18:00+0000\n"
"Last-Translator: Patrick Chilton <chpatrick@gmail.com>\n"
"Language-Team: Hungarian <https://translate.pretix.eu/projects/pretix/pretix/"
"hu/>\n"
@@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.10.4\n"
"X-Generator: Weblate 5.11.4\n"
#: pretix/_base_settings.py:87
msgid "English"
@@ -14670,7 +14670,7 @@ msgstr "A kiválasztott \"{seat}\" ülés nem elérhető."
#: pretix/control/logdisplay.py:406 pretix/control/views/orders.py:1573
#: pretix/presale/views/order.py:1047
msgid "The order has been canceled."
msgstr ""
msgstr "A megrendelés sztornózva lett."
#: pretix/control/logdisplay.py:414
#, python-brace-format

File diff suppressed because it is too large Load Diff

View File

@@ -8,17 +8,17 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-04-28 11:08+0000\n"
"PO-Revision-Date: 2019-08-27 08:00+0000\n"
"Last-Translator: Bostjan Marusic <bostjan@brokenbones.si>\n"
"Language-Team: Slovenian <https://translate.pretix.eu/projects/pretix/pretix-"
"js/sl/>\n"
"PO-Revision-Date: 2025-05-16 17:00+0000\n"
"Last-Translator: bstramsek <stramsek.borut+pretix-translate@gmail.com>\n"
"Language-Team: Slovenian <https://translate.pretix.eu/projects/pretix/"
"pretix-js/sl/>\n"
"Language: sl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || "
"n%100==4 ? 2 : 3;\n"
"X-Generator: Weblate 3.5.1\n"
"X-Generator: Weblate 5.11.4\n"
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
@@ -32,7 +32,7 @@ msgstr "Komentar:"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:34
msgid "PayPal"
msgstr ""
msgstr "PayPal"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:35
msgid "Venmo"

View File

@@ -1,5 +1,4 @@
{% load i18n %}
{% load icon %}
{% load eventurl %}
{% if item.current_unavailability_reason == 'require_voucher' %}
@@ -21,15 +20,14 @@
{% elif avail <= 10 %}
<div class="col-md-2 col-sm-3 col-xs-6 availability-box gone">
{% if price or original_price %}
<strong>{% icon "ban" %} {% trans "SOLD OUT" %}</strong>
<strong>{% trans "SOLD OUT" %}</strong>
{% else %}
<strong>{% icon "ban" %} {% trans "FULLY BOOKED" %}</strong>
<strong>{% trans "FULLY BOOKED" %}</strong>
{% endif %}
</span>
{% if allow_waitinglist and item.allow_waitinglist %}
<br/>
<a href="{% eventurl event "presale:event.waitinglist" cart_namespace=cart_namespace|default_if_none:"" %}?item={{ item.pk }}{% if var %}&var={{ var.pk }}{% endif %}{% if subevent %}&subevent={{ subevent.pk }}{% endif %}" class="btn btn-default btn-xs">
{% icon "plus-circle" %}
<a href="{% eventurl event "presale:event.waitinglist" cart_namespace=cart_namespace|default_if_none:"" %}?item={{ item.pk }}{% if var %}&var={{ var.pk }}{% endif %}{% if subevent %}&subevent={{ subevent.pk }}{% endif %}">
<span class="fa fa-plus-circle" aria-hidden="true"></span>
{% trans "Waiting list" %}
</a>
{% endif %}

View File

@@ -492,15 +492,21 @@
<div class="row">
<div class="col-md-12">
{% if not cart.is_ordered %}
<p class="text-muted" id="cart-deadline" data-expires="{{ cart.first_expiry|date:"Y-m-d H:i:sO" }}">
{% if cart.minutes_left > 0 or cart.seconds_left > 0 %}
{% blocktrans trimmed with minutes=cart.minutes_left %}
The items in your cart are reserved for you for {{ minutes }} minutes.
{% endblocktrans %}
{% else %}
{% trans "The items in your cart are no longer reserved for you. You can still complete your order as long as theyre available." %}
{% endif %}
</p>
<form class="text-muted"
method="post" data-asynctask action="{% eventurl request.event "presale:event.cart.extend" cart_namespace=cart_namespace %}">
{% csrf_token %}
<span id="cart-deadline" data-expires="{{ cart.first_expiry|date:"Y-m-d H:i:sO" }}">
{% if cart.minutes_left > 0 or cart.seconds_left > 0 %}
{% blocktrans trimmed with minutes=cart.minutes_left %}
The items in your cart are reserved for you for {{ minutes }} minutes.
{% endblocktrans %}
{% else %}
{% trans "The items in your cart are no longer reserved for you. You can still complete your order as long as theyre available." %}
{% endif %}
</span>
<button class="btn btn-link" type="submit" id="cart-extend-button">
<i class="fa fa-refresh" aria-hidden="true"></i> {% trans "Extend" %}</button>
</form>
{% else %}
<p class="sr-only" id="cart-description">{% trans "Overview of your ordered products." %}</p>
{% endif %}

View File

@@ -1,5 +1,4 @@
{% load i18n %}
{% load icon %}
{% load l10n %}
{% load eventurl %}
{% load money %}
@@ -83,9 +82,9 @@
{% if not event.settings.show_variations_expanded %}
{% if item.best_variation_availability <= 10 %}
{% if not item.min_price %}
<strong class="gone">{% icon "ban" %} {% trans "FULLY BOOKED" %}</strong>
<strong class="gone">{% trans "FULLY BOOKED" %}</strong>
{% else %}
<strong class="gone">{% icon "ban" %} {% trans "SOLD OUT" %}</strong>
<strong class="gone">{% trans "SOLD OUT" %}</strong>
{% endif %}
{% if allow_waitinglist and item.allow_waitinglist %}
<br/>

View File

@@ -7,6 +7,8 @@
{% load thumb %}
{% load eventsignal %}
{% load rich_text %}
{% load icon %}
{% load dialog %}
{% block title %}
{% if "year" in request.GET %}
@@ -240,6 +242,13 @@
</div>
{% endif %}
</form>
{% if ev.presale_is_running and display_add_to_cart %}
{% trans "You didnt select any ticket." as label_nothing_to_add %}
{% trans "Please tick a checkbox or enter a quantity for one of the ticket types to add to the cart." as description_nothing_to_add %}
{% dialog "dialog-nothing-to-add" label_nothing_to_add description_nothing_to_add icon="exclamation-circle" %}
<p class="modal-card-confirm"><button class="btn btn-primary">{% trans "OK" %}</button></p>
{% enddialog %}
{% endif %}
{% endif %}
{% endif %}
</main>

View File

@@ -2,6 +2,8 @@
{% load rich_text %}
{% load safelink %}
{% load escapejson %}
{% load icon %}
{% load dialog %}
<div id="ajaxerr">
</div>
<div id="popupmodal" hidden aria-live="polite">
@@ -50,93 +52,74 @@
{{ cookie_consent_from_widget|json_script:"cookie-consent-from-widget" }}
{% endif %}
{% if cookie_providers %}
<div id="cookie-consent-modal" aria-live="polite">
<div class="modal-card">
<div class="modal-card-content">
<h3 id="cookie-consent-modal-label"></h3>
<div id="cookie-consent-modal-description">
<form class="form-horizontal">
{% with request.event|default:request.organizer as sh %}
<h3>{{ sh.settings.cookie_consent_dialog_title }}</h3>
{{ sh.settings.cookie_consent_dialog_text|rich_text }}
{% if sh.settings.cookie_consent_dialog_text_secondary %}
<div class="text-muted">
{{ sh.settings.cookie_consent_dialog_text_secondary|rich_text }}
</div>
{% endif %}
<details id="cookie-consent-details">
<summary>
<span class="fa fa-fw chevron"></span>
{% trans "Adjust settings in detail" %}
</summary>
<div class="checkbox">
<label>
<input type="checkbox" disabled checked="" aira-describedby="cookie-consent-checkbox-required-description">
{% trans "Required cookies" %}
</label>
</div>
<div class="help-block" id="cookie-consent-checkbox-required-description">
<p>{% trans "Functional cookies (e.g. shopping cart, login, payment, language preference) and technical cookies (e.g. security purposes)" %}</p>
</div>
{% for cp in cookie_providers %}
<div class="checkbox">
<label>
<input type="checkbox" name="{{ cp.identifier }}" aira-describedby="cookie-consent-checkbox-{{ cp.identifier }}-description">
{{ cp.provider_name }}
</label>
</div>
<div class="help-block" id="cookie-consent-checkbox-{{ cp.identifier }}-description">
<p>
{% for c in cp.usage_classes %}
{% if forloop.counter0 > 0 %}&middot; {% endif %}
{% if c.value == 1 %}
{% trans "Functionality" context "cookie_usage" %}
{% elif c.value == 2 %}
{% trans "Analytics" context "cookie_usage" %}
{% elif c.value == 3 %}
{% trans "Marketing" context "cookie_usage" %}
{% elif c.value == 4 %}
{% trans "Social features" context "cookie_usage" %}
{% endif %}
{% endfor %}
{% if cp.privacy_url %}
&middot;
<a href="{% safelink cp.privacy_url %}" target="_blank">
{% trans "Privacy policy" %}
</a>
{% endif %}
</p>
</div>
{% endfor %}
</details>
<div class="row">
<div class="col-xs-12 col-md-6">
<p>
<button type="button" class="btn btn-lg btn-block btn-primary" id="cookie-consent-button-no"
data-summary-text="{{ sh.settings.cookie_consent_dialog_button_no }}"
data-detail-text="{% trans "Save selection" %}">
{{ sh.settings.cookie_consent_dialog_button_no }}
</button>
</p>
</div>
<div class="col-xs-12 col-md-6">
<p>
<button type="button" class="btn btn-lg btn-block btn-primary" id="cookie-consent-button-yes">
{{ sh.settings.cookie_consent_dialog_button_yes }}
</button>
</p>
</div>
</div>
{% if sh.settings.privacy_url %}
<p class="text-center">
<a href="{% safelink sh.settings.privacy_url %}" target="_blank" rel="noopener">{% trans "Privacy policy" %}</a>
</p>
{% endif %}
{% endwith %}
<form>
{% with request.event|default:request.organizer as sh %}
{% dialog "cookie-consent-modal" sh.settings.cookie_consent_dialog_title sh.settings.cookie_consent_dialog_text|rich_text icon="shield" %}
{% if sh.settings.cookie_consent_dialog_text_secondary %}
<div class="text-muted">
{{ sh.settings.cookie_consent_dialog_text_secondary|rich_text }}
</div>
</div>
</div>
</div>
{% endif %}
<details id="cookie-consent-details">
<summary>
<span class="fa fa-fw chevron"></span>
{% trans "Adjust settings in detail" %}
</summary>
<div class="checkbox">
<label>
<input type="checkbox" disabled checked="" aira-describedby="cookie-consent-checkbox-required-description">
{% trans "Required cookies" %}
</label>
</div>
<div class="help-block" id="cookie-consent-checkbox-required-description">
<p>{% trans "Functional cookies (e.g. shopping cart, login, payment, language preference) and technical cookies (e.g. security purposes)" %}</p>
</div>
{% for cp in cookie_providers %}
<div class="checkbox">
<label>
<input type="checkbox" name="{{ cp.identifier }}" aira-describedby="cookie-consent-checkbox-{{ cp.identifier }}-description">
{{ cp.provider_name }}
</label>
</div>
<div class="help-block" id="cookie-consent-checkbox-{{ cp.identifier }}-description">
<p>
{% for c in cp.usage_classes %}
{% if forloop.counter0 > 0 %}&middot; {% endif %}
{% if c.value == 1 %}
{% trans "Functionality" context "cookie_usage" %}
{% elif c.value == 2 %}
{% trans "Analytics" context "cookie_usage" %}
{% elif c.value == 3 %}
{% trans "Marketing" context "cookie_usage" %}
{% elif c.value == 4 %}
{% trans "Social features" context "cookie_usage" %}
{% endif %}
{% endfor %}
{% if cp.privacy_url %}
&middot;
<a href="{% safelink cp.privacy_url %}" target="_blank">
{% trans "Privacy policy" %}
</a>
{% endif %}
</p>
</div>
{% endfor %}
</details>
<p class="modal-card-confirm modal-card-confirm-spread">
<button class="btn btn-lg btn-default" id="cookie-consent-button-no" value="no" autofocus="true"
data-summary-text="{{ sh.settings.cookie_consent_dialog_button_no }}"
data-detail-text="{% trans "Save selection" %}">
{{ sh.settings.cookie_consent_dialog_button_no }}
</button>
<button class="btn btn-lg btn-primary" id="cookie-consent-button-yes" value="yes">
{{ sh.settings.cookie_consent_dialog_button_yes }}
</button>
</p>
{% if sh.settings.privacy_url %}
<p class="text-center">
<small><a href="{% safelink sh.settings.privacy_url %}" target="_blank" rel="noopener">{% trans "Privacy policy" %}</a></small>
</p>
{% endif %}
{% enddialog %}
{% endwith %}
{% endif %}
{% endif %}

View File

@@ -49,7 +49,7 @@
<div class="event-list full-width-list alternating-rows">
{% for e in events %}{% eventurl e "presale:event.index" as url %}
<article class="row" aria-labelledby="event-{{ e.pk }}-label" aria-describedby="event-{{ e.pk }}-desc">
<h3 class="col-md-4 col-xs-12"><a href="{{ url }}" id="event-{{ e.pk }}-label">{{ e.name }}</a></h3>
<h3 class="col-md-4 col-xs-12"><a href="{{ url }}" id="event-{{ e.pk }}-label" class="no-underline">{{ e.name }}</a></h3>
<p class="col-md-3 col-xs-12" id="event-{{ e.pk }}-desc">
{% if e.settings.show_dates_on_frontpage %}
{% if e.has_subevents %}

View File

@@ -56,6 +56,7 @@ frame_wrapped_urls = [
re_path(r'^cart/remove$', pretix.presale.views.cart.CartRemove.as_view(), name='event.cart.remove'),
re_path(r'^cart/voucher$', pretix.presale.views.cart.CartApplyVoucher.as_view(), name='event.cart.voucher'),
re_path(r'^cart/clear$', pretix.presale.views.cart.CartClear.as_view(), name='event.cart.clear'),
re_path(r'^cart/extend$', pretix.presale.views.cart.CartExtendReservation.as_view(), name='event.cart.extend'),
re_path(r'^cart/answer/(?P<answer>[^/]+)/$',
pretix.presale.views.cart.AnswerDownload.as_view(),
name='event.cart.download.answer'),

View File

@@ -62,7 +62,7 @@ from pretix.base.models import (
)
from pretix.base.services.cart import (
CartError, add_items_to_cart, apply_voucher, clear_cart, error_messages,
remove_cart_position,
extend_cart_reservation, remove_cart_position,
)
from pretix.base.timemachine import time_machine_now
from pretix.base.views.tasks import AsyncAction
@@ -537,6 +537,20 @@ class CartClear(EventViewMixin, CartActionMixin, AsyncAction, View):
request.sales_channel.identifier, time_machine_now(default=None))
@method_decorator(allow_frame_if_namespaced, 'dispatch')
class CartExtendReservation(EventViewMixin, CartActionMixin, AsyncAction, View):
task = extend_cart_reservation
known_errortypes = ['CartError']
def get_success_message(self, value):
if value > 0:
return _('Your cart timeout was extended.')
def post(self, request, *args, **kwargs):
return self.do(self.request.event.id, get_or_create_cart_id(self.request), translation.get_language(),
request.sales_channel.identifier, time_machine_now(default=None))
@method_decorator(allow_cors_if_namespaced, 'dispatch')
@method_decorator(allow_frame_if_namespaced, 'dispatch')
@method_decorator(iframe_entry_view_wrapper, 'dispatch')

Binary file not shown.

After

Width:  |  Height:  |  Size: 587 B

View File

@@ -49,7 +49,7 @@ html.rtl {
}
input[lang=ar], textarea[lang=ar], div[lang=ar], pre[lang=ar],
input[lang=hw], textarea[lang=hw], div[lang=hw], pre[lang=hw] {
input[lang=he], textarea[lang=he], div[lang=he], pre[lang=he] {
/* Keep list of languages in sync with pretix._base_settings.LANGUAGES_RTL */
direction: rtl;
}
}

View File

@@ -262,3 +262,95 @@ svg.svg-icon {
@include table-row-variant('info', var(--pretix-brand-info-success-lighten-30), var(--pretix-brand-info-success-lighten-25));
@include table-row-variant('warning', var(--pretix-brand-warning-lighten-40), var(--pretix-brand-warning-lighten-35));
@include table-row-variant('danger', var(--pretix-brand-danger-lighten-30), var(--pretix-brand-danger-lighten-25));
dialog {
border: none;
width: 80%;
max-width: 43em;
padding: 0;
box-shadow: 0 7px 14px 0 rgba(78, 50, 92, 0.1),0 3px 6px 0 rgba(0,0,0,.07);
background: white;
border-radius: $border-radius-large;
opacity: 0;
transition: opacity .5s allow-discrete;
.modal-card {
display: flex;
flex-direction: column;
align-content: stretch;
}
.modal-card-icon {
background: $brand-primary;
font-size: 2em;
color: white;
text-align: center;
padding: 3px;
}
.modal-card-content {
padding: 1.5em;
}
.modal-card-content>*:last-child {
margin-bottom: 0;
}
.modal-card-content>*:first-child {
margin-top: 0;
}
.modal-card-confirm {
margin-top: 2em;
display: flex;
justify-content: flex-end;
gap: 1em;
align-items: center;
}
.modal-card-confirm-spread {
justify-content: space-between;
}
}
dialog::backdrop {
background-color: rgba(255, 255, 255, .5);
opacity: 0;
transition: opacity .5s allow-discrete;
}
dialog[open], dialog[open]::backdrop {
opacity: 1;
}
@starting-style {
dialog[open], dialog[open]::backdrop {
opacity: 0;
}
}
@media screen and (min-width: $screen-sm-min) {
dialog {
.modal-card:has(.modal-card-icon) {
flex-direction: row;
}
.modal-card-content {
padding: 2em;
}
.modal-card-icon {
font-size: 4em;
padding: 6px 16px;
}
}
}
.shake-once {
animation: shake .2s;
transform: translate3d(0, 0, 0);
backface-visibility: hidden;
}
@keyframes shake {
0% { transform: skewX(0deg); }
20% { transform: skewX(-5deg); }
40% { transform: skewX(5deg); }
60% { transform: skewX(-5deg); }
80% { transform: skewX(5deg); }
100% { transform: skewX(0deg); }
}

View File

@@ -160,7 +160,7 @@ pre[lang=ht], input[lang=ht], textarea[lang=ht], div[lang=ht] { background-image
pre[lang=hu], input[lang=hu], textarea[lang=hu], div[lang=hu] { background-image: url(static('pretixbase/img/flags/hu.png')); }
pre[lang=id], input[lang=id], textarea[lang=id], div[lang=id] { background-image: url(static('pretixbase/img/flags/id.png')); }
pre[lang=ie], input[lang=ie], textarea[lang=ie], div[lang=ie] { background-image: url(static('pretixbase/img/flags/ie.png')); }
pre[lang=il], input[lang=il], textarea[lang=il], div[lang=il] { background-image: url(static('pretixbase/img/flags/il.png')); }
pre[lang=he], input[lang=he], textarea[lang=he], div[lang=he] { background-image: url(static('pretixbase/img/flags/il.png')); }
pre[lang=in], input[lang=in], textarea[lang=in], div[lang=in] { background-image: url(static('pretixbase/img/flags/in.png')); }
pre[lang=io], input[lang=io], textarea[lang=io], div[lang=io] { background-image: url(static('pretixbase/img/flags/io.png')); }
pre[lang=iq], input[lang=iq], textarea[lang=iq], div[lang=iq] { background-image: url(static('pretixbase/img/flags/iq.png')); }
@@ -306,3 +306,4 @@ pre[lang=za], input[lang=za], textarea[lang=za], div[lang=za] { background-image
pre[lang=zm], input[lang=zm], textarea[lang=zm], div[lang=zm] { background-image: url(static('pretixbase/img/flags/zm.png')); }
pre[lang=zw], input[lang=zw], textarea[lang=zw], div[lang=zw] { background-image: url(static('pretixbase/img/flags/zw.png')); }
pre[lang=en], input[lang=en], textarea[lang=en], div[lang=en] { background-image: url(static('pretixbase/img/flags/gb.png')); }
pre[lang=eu], input[lang=eu], textarea[lang=eu], div[lang=eu] { background-image: url(static('pretixbase/img/flags/basque.png')); }

View File

@@ -55,6 +55,7 @@ var cart = {
pad(diff_minutes.toString(), 2) + ':' + pad(diff_seconds.toString(), 2)
);
}
$("#cart-extend-button").toggle(diff_minutes < 3);
},
init: function () {

View File

@@ -6,7 +6,7 @@ $(function () {
var storage_key = $("#cookie-consent-storage-key").text();
var widget_consent = $("#cookie-consent-from-widget").text();
var consent_checkboxes = $("#cookie-consent-details input[type=checkbox][name]");
var consent_modal = $("#cookie-consent-modal");
var consent_modal = document.getElementById("cookie-consent-modal");
function update_consent(consent, sessionOnly) {
if (storage_key && window.sessionStorage && sessionOnly) {
@@ -108,25 +108,32 @@ $(function () {
_set_button_text();
if (show_dialog) {
// We use .css() instead of .show() because of some weird issue that only occurs in Firefox
// and only within the widget.
consent_modal.css("display", "block");
consent_modal.showModal();
consent_modal.addEventListener("cancel", function() {
// Dialog was initially shown, interpret Escape as „do not consent to new providers“
var consent = {};
consent_checkboxes.each(function () {
consent[this.name] = storage_val[this.name] || false;
});
update_consent(consent, false);
}, {once : true});
}
$("#cookie-consent-button-yes, #cookie-consent-button-no").on("click", function () {
consent_modal.hide();
consent_modal.addEventListener("close", function () {
if (!consent_modal.returnValue) {// ESC, do not save
return;
}
var consent = {};
var consent_all = this.id == "cookie-consent-button-yes";
var consent_all = consent_modal.returnValue == "yes";
consent_checkboxes.each(function () {
consent[this.name] = this.checked = consent_all || this.checked;
});
if (consent_all) _set_button_text();
// Always save explicit consent to permanent storage
update_consent(consent, false);
});
consent_checkboxes.on("change", _set_button_text);
$("#cookie-consent-reopen").on("click", function (e) {
consent_modal.show()
consent_modal.showModal()
e.preventDefault()
return true
})

View File

@@ -390,6 +390,13 @@ $(function () {
sessionStorage.removeItem('scrollpos');
}
$("dialog").on("mousedown", function (e) {
if (e.target == this) {
// dialog has no padding, so this triggers only onclick on backdrop
this.close();
}
});
$(".accordion-radio").click(function() {
var $input = $("input", this);
if (!$input.prop("checked")) $input.prop('checked', true).trigger("change");
@@ -478,33 +485,21 @@ $(function () {
sessionStorage.setItem('scrollpos', window.scrollY);
});
}
var update_cart_form = function () {
var is_enabled = $(".product-row input[type=checkbox]:checked, .variations input[type=checkbox]:checked, .product-row input[type=radio]:checked, .variations input[type=radio]:checked").length;
if (!is_enabled) {
$(".input-item-count").each(function () {
if ($(this).val() && $(this).val() !== "0") {
is_enabled = true;
}
});
$(".input-seat-selection option").each(function() {
if ($(this).val() && $(this).val() !== "" && $(this).prop('selected')) {
is_enabled = true;
}
});
$("form:has(#btn-add-to-cart)").on("submit", function(e) {
if (
(this.classList.contains("has-seating") && this.querySelector("pretix-seating-checkout-button button")) ||
this.querySelector("input[type=checkbox]:checked") ||
[...this.querySelectorAll(".input-item-count[type=text]")].some(input => input.value && input.value !== "0") // TODO: seating hat noch einen seating-dummy-item-count, das ist Mist!
) {
// okay, let the submit-event bubble to async-task
return;
}
if (!is_enabled && (!$(".has-seating").length || $("#seating-dummy-item-count").length)) {
$("#btn-add-to-cart").prop("disabled", !is_enabled).popover({
'content': function () { return gettext("Please enter a quantity for one of the ticket types.") },
'placement': 'top',
'trigger': 'hover focus'
});
} else {
$("#btn-add-to-cart").prop("disabled", false).popover("destroy")
}
};
update_cart_form();
$(".product-row input[type=checkbox], .variations input[type=checkbox], .product-row input[type=radio], .variations input[type=radio], .input-item-count, .input-seat-selection")
.on("change mouseup keyup", update_cart_form);
e.preventDefault();
e.stopPropagation();
document.querySelector("#dialog-nothing-to-add").showModal();
});
$(".table-calendar td.has-events").click(function () {
var $grid = $(this).closest("[role='grid']");

View File

@@ -471,7 +471,7 @@ Vue.component('variation', {
// Variation description
+ '<div class="pretix-widget-item-info-col">'
+ '<div class="pretix-widget-item-title-and-description">'
+ '<strong :id="variation_label_id" class="pretix-widget-item-title">{{ variation.value }}</strong>'
+ '<strong :id="variation_label_id" class="pretix-widget-item-title" role="heading" v-bind:aria-level="headingLevel">{{ variation.value }}</strong>'
+ '<div :id="variation_desc_id" class="pretix-widget-item-description" v-if="variation.description" v-html="variation.description"></div>'
+ '<p class="pretix-widget-item-meta" '
+ ' v-if="!variation.has_variations && variation.avail[1] !== null && variation.avail[0] === 100">'
@@ -500,6 +500,7 @@ Vue.component('variation', {
props: {
variation: Object,
item: Object,
category: Object,
},
computed: {
orig_price: function () {
@@ -523,6 +524,9 @@ Vue.component('variation', {
aria_labelledby: function () {
return [this.variation_label_id, this.variation_price_id].join(" ");
},
headingLevel: function () {
return this.category.name ? '5' : '4';
},
}
});
Vue.component('item', {
@@ -533,12 +537,12 @@ Vue.component('item', {
+ '<div class="pretix-widget-item-info-col">'
+ '<a :href="item.picture_fullsize" v-if="item.picture" class="pretix-widget-item-picture-link" @click.prevent.stop="lightbox"><img :src="item.picture" class="pretix-widget-item-picture" :alt="picture_alt_text"></a>'
+ '<div class="pretix-widget-item-title-and-description">'
+ '<a v-if="item.has_variations && show_toggle" :id="item_label_id" class="pretix-widget-item-title" :href="\'#\' + item.id + \'-variants\'"'
+ '<a v-if="item.has_variations && show_toggle" :id="item_label_id" role="heading" v-bind:aria-level="headingLevel" class="pretix-widget-item-title" :href="\'#\' + item.id + \'-variants\'"'
+ ' @click.prevent.stop="expand"'
+ '>'
+ '{{ item.name }}'
+ '</a>'
+ '<strong v-else class="pretix-widget-item-title" :id="item_label_id">{{ item.name }}</strong>'
+ '<strong v-else class="pretix-widget-item-title" :id="item_label_id" role="heading" v-bind:aria-level="headingLevel">{{ item.name }}</strong>'
+ '<div class="pretix-widget-item-description" :id="item_desc_id" v-if="item.description" v-html="item.description"></div>'
+ '<p class="pretix-widget-item-meta" v-if="item.order_min && item.order_min > 1">'
+ '<small>{{ min_order_str }}</small>'
@@ -572,13 +576,14 @@ Vue.component('item', {
// Variations
+ '<div :class="varClasses" v-if="item.has_variations" :id="item.id + \'-variants\'" ref="variations">'
+ '<variation v-for="variation in item.variations" :variation="variation" :item="item" :key="variation.id">'
+ '<variation v-for="variation in item.variations" :variation="variation" :item="item" :category="category" :key="variation.id">'
+ '</variation>'
+ '</div>'
+ '</div>'),
props: {
item: Object,
category: Object,
},
data: function () {
return {
@@ -636,6 +641,9 @@ Vue.component('item', {
picture_alt_text: function () {
return django.interpolate(strings["image_of"], [this.item.name]);
},
headingLevel: function () {
return this.category.name ? '4' : '3';
},
item_label_id: function () {
return this.$root.html_id + '-item-label-' + this.item.id;
},
@@ -687,7 +695,7 @@ Vue.component('category', {
+ '<div class="pretix-widget-category-description" v-if="category.description" v-html="category.description">'
+ '</div>'
+ '<div class="pretix-widget-category-items">'
+ '<item v-for="item in category.items" :item="item" :key="item.id"></item>'
+ '<item v-for="item in category.items" :category="category" :item="item" :key="item.id"></item>'
+ '</div>'
+ '</div>'),
props: {
@@ -1002,7 +1010,7 @@ Vue.component('pretix-widget-event-form', {
// Event name
+ '<div class="pretix-widget-event-header" v-if="display_event_info">'
+ '<strong>{{ $root.name }}</strong>'
+ '<strong role="heading" aria-level="2">{{ $root.name }}</strong>'
+ '</div>'
// Date range

View File

@@ -23,7 +23,7 @@
text-align: center;
&.gone, .gone {
color: $brand-danger;
color: $alert-danger-text;
}
&.unavailable, .unavailable {
color: $alert-warning-text;

View File

@@ -76,6 +76,7 @@ html {
clip: auto;
}
#skip-to-main {
z-index: 9999;
/* padding is needed to make focus-outline visible */
padding: 8px;
background-color: inherit;
@@ -156,6 +157,7 @@ button:focus, a:focus, .btn:focus, summary:focus, div:focus,
button:active:focus, a:active:focus, .btn:active:focus, summary:active:focus, div:active:focus,
button.active:focus, a.active:focus, .btn.active:focus,
input:focus, .form-control:focus, .btn-checkbox:has(input:focus),
input[type="file"]:focus, input[type="radio"]:focus, input[type="checkbox"]:focus,
.input-item-count-group:has(input:focus), .input-group-price:has(input:focus) {
outline: 2px solid $link-hover-color;
outline-offset: 2px;
@@ -289,7 +291,7 @@ body.loading .container {
font-size: 120px;
color: $brand-primary;
}
#loadingmodal, #ajaxerr, #cookie-consent-modal, #popupmodal {
#loadingmodal, #ajaxerr, #popupmodal {
position: fixed;
top: 0;
left: 0;
@@ -359,7 +361,7 @@ body.loading .container {
}
}
@media (max-width: 700px) {
#loadingmodal, #ajaxerr, #cookie-consent-modal, #popupmodal {
#loadingmodal, #ajaxerr, #popupmodal {
.modal-card {
margin: 25px auto 0;
max-height: calc(100vh - 50px - 20px);

File diff suppressed because it is too large Load Diff