mirror of
https://github.com/pretix/pretix.git
synced 2025-12-05 21:32:28 +00:00
Compare commits
18 Commits
v2025.9.2
...
a11y-add-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0368d548a0 | ||
|
|
1c2df6eaae | ||
|
|
fddeff9530 | ||
|
|
f96e10011a | ||
|
|
a7c80872cd | ||
|
|
587c3f8c8b | ||
|
|
9bc9fd2eb5 | ||
|
|
44ddeac4d1 | ||
|
|
fdb2c0e313 | ||
|
|
1a68c30413 | ||
|
|
68222b2fe3 | ||
|
|
d17777da9b | ||
|
|
498e8a52af | ||
|
|
c7db1eb1d7 | ||
|
|
6d56ed6e46 | ||
|
|
80a5c4ac5b | ||
|
|
51d1d1fbc1 | ||
|
|
37a9ecc61f |
60
src/pretix/base/templatetags/dialog.py
Normal file
60
src/pretix/base/templatetags/dialog.py
Normal 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>
|
||||
""")
|
||||
@@ -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 didn’t 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>
|
||||
|
||||
@@ -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 %}· {% 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 %}
|
||||
·
|
||||
<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 %}· {% 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 %}
|
||||
·
|
||||
<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 %}
|
||||
|
||||
@@ -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); }
|
||||
}
|
||||
|
||||
@@ -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
|
||||
})
|
||||
|
||||
@@ -478,33 +478,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']");
|
||||
|
||||
@@ -289,7 +289,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 +359,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);
|
||||
|
||||
Reference in New Issue
Block a user