First steps

This commit is contained in:
Raphael Michel
2021-11-20 12:08:47 +01:00
parent 6f0f4755ef
commit 202f34ad5b
11 changed files with 265 additions and 7 deletions
+8 -1
View File
@@ -296,7 +296,14 @@ class OrganizerSettingsSerializer(SettingsSerializer):
'theme_round_borders',
'primary_font',
'organizer_logo_image_inherit',
'organizer_logo_image'
'organizer_logo_image',
'privacy_url',
'cookie_consent',
'cookie_consent_dialog_title',
'cookie_consent_dialog_text',
'cookie_consent_dialog_text_secondary',
'cookie_consent_dialog_button_yes',
'cookie_consent_dialog_button_no',
]
def __init__(self, *args, **kwargs):
+12 -1
View File
@@ -97,10 +97,21 @@ class Organizer(LoggedModel):
return self.name
def save(self, *args, **kwargs):
is_new = not self.pk
obj = super().save(*args, **kwargs)
self.get_cache().clear()
if is_new:
self.set_defaults()
else:
self.get_cache().clear()
return obj
def set_defaults(self):
"""
This will be called after organizer creation.
This way, we can use this to introduce new default settings to pretix that do not affect existing organizers.
"""
self.settings.cookie_consent = True
def get_cache(self):
"""
Returns an :py:class:`ObjectRelatedCache` object. This behaves equivalent to
+83
View File
@@ -1512,6 +1512,17 @@ DEFAULTS = {
),
'serializer_class': serializers.URLField,
},
'privacy_url': {
'default': None,
'type': str,
'form_class': forms.URLField,
'form_kwargs': dict(
label=_("Privacy Policy URL"),
help_text=_("This should point e.g. to a part of your website that explains how you use data gathered in "
"your ticket shop."),
),
'serializer_class': serializers.URLField,
},
'confirm_texts': {
'default': LazyI18nStringList(),
'type': LazyI18nStringList,
@@ -2489,6 +2500,78 @@ Your {organizer} team"""))
'many years. If you keep it empty, gift cards do not have an explicit expiry date.'),
)
},
'cookie_consent': {
'default': 'False',
'form_class': forms.BooleanField,
'serializer_class': serializers.BooleanField,
'form_kwargs': dict(
label=_("Enable cookie consent management features"),
),
'type': bool,
},
'cookie_consent_dialog_text': {
'default': LazyI18nString.from_gettext(gettext_noop(
'By clicking "Accept all cookies", you agree to the storing of cookies and use of similar technologies on '
'your device.'
)),
'type': LazyI18nString,
'serializer_class': I18nField,
'form_class': I18nFormField,
'form_kwargs': dict(
label=_("Dialog text"),
widget=I18nTextarea,
widget_kwargs={'attrs': {'rows': '3', 'data-display-dependency': '#id_settings-cookie_consent'}},
)
},
'cookie_consent_dialog_text_secondary': {
'default': LazyI18nString.from_gettext(gettext_noop(
'We use cookies and similar technologies to gather data that allows us to improve this website and our '
'offerings. If you do not agree, we will only use cookies if they are essential to providing the services '
'this website offers. This includes cookies used to store your cart and personal settings, cookies used for '
'payment processing, as well as cookies required for technical or security reasons.'
)),
'type': LazyI18nString,
'serializer_class': I18nField,
'form_class': I18nFormField,
'form_kwargs': dict(
label=_("Secondary dialog text"),
widget=I18nTextarea,
widget_kwargs={'attrs': {'rows': '3', 'data-display-dependency': '#id_settings-cookie_consent'}},
)
},
'cookie_consent_dialog_title': {
'default': LazyI18nString.from_gettext(gettext_noop('Privacy settings')),
'type': LazyI18nString,
'serializer_class': I18nField,
'form_class': I18nFormField,
'form_kwargs': dict(
label=_('Dialog title'),
widget=I18nTextInput,
widget_kwargs={'attrs': {'data-display-dependency': '#id_settings-cookie_consent'}},
)
},
'cookie_consent_dialog_button_yes': {
'default': LazyI18nString.from_gettext(gettext_noop('Accept all cookies')),
'type': LazyI18nString,
'serializer_class': I18nField,
'form_class': I18nFormField,
'form_kwargs': dict(
label=_('"Accept" button description'),
widget=I18nTextInput,
widget_kwargs={'attrs': {'data-display-dependency': '#id_settings-cookie_consent'}},
)
},
'cookie_consent_dialog_button_no': {
'default': LazyI18nString.from_gettext(gettext_noop('Required cookies only')),
'type': LazyI18nString,
'serializer_class': I18nField,
'form_class': I18nFormField,
'form_kwargs': dict(
label=_('"Reject" button description'),
widget=I18nTextInput,
widget_kwargs={'attrs': {'data-display-dependency': '#id_settings-cookie_consent'}},
)
},
'seating_choice': {
'default': 'True',
'form_class': forms.BooleanField,
+8 -2
View File
@@ -307,8 +307,14 @@ class OrganizerSettingsForm(SettingsForm):
'theme_color_danger',
'theme_color_background',
'theme_round_borders',
'primary_font'
'primary_font',
'privacy_url',
'cookie_consent',
'cookie_consent_dialog_title',
'cookie_consent_dialog_text',
'cookie_consent_dialog_text_secondary',
'cookie_consent_dialog_button_yes',
'cookie_consent_dialog_button_no',
]
organizer_logo_image = ExtFileField(
@@ -82,6 +82,49 @@
{% bootstrap_field sform.giftcard_expiry_years layout="control" %}
{% bootstrap_field sform.giftcard_length layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Privacy" %}</legend>
{% bootstrap_field sform.privacy_url layout="control" %}
<div class="alert alert-legal">
<p>
{% blocktrans trimmed %}
Some jurisdictions, including the European Union, require user consent before you
are allowed to use cookies or similar technology for analytics, tracking, payment,
or similar purposes.
{% endblocktrans %}
</p>
<p>
{% blocktrans trimmed %}
pretix itself only ever sets cookies that are required to provide the service
requested by the user or to maintain an appropriate level of security. Therefore,
cookies set by pretix itself do not require consent in all jurisdictions that we
are aware of.
{% endblocktrans %}
</p>
<p>
{% blocktrans trimmed %}
Therefore, the settings on this page will <strong>only</strong> have an affect
if you use <strong>plugins</strong> that require additional cookies
<strong>and</strong> participate in our cookie consent mechanism.
{% endblocktrans %}
</p>
<p>
<strong>{% blocktrans trimmed %}
Ultimately, it is your responsibility to make sure you comply with all relevant
laws. We try to help by providing these settings, but we cannot assume liability
since we do not know the exact configuration of your pretix usage, the legal details
in your specific jurisdiction, or the agreements you have with third parties such as
payment or tracking providers.
{% endblocktrans %}</strong>
</p>
</div>
{% bootstrap_field sform.cookie_consent layout="control" %}
{% bootstrap_field sform.cookie_consent_dialog_title layout="control" %}
{% bootstrap_field sform.cookie_consent_dialog_text layout="control" %}
{% bootstrap_field sform.cookie_consent_dialog_text_secondary layout="control" %}
{% bootstrap_field sform.cookie_consent_dialog_button_yes layout="control" %}
{% bootstrap_field sform.cookie_consent_dialog_button_no layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Invoices" %}</legend>
{% bootstrap_field sform.invoice_regenerate_allowed layout="control" %}
@@ -169,6 +169,12 @@
{% if request.event.settings.contact_mail %}
<li><a href="mailto:{{ request.event.settings.contact_mail }}">{% trans "Contact event organizer" %}</a></li>
{% endif %}
{% if request.event.settings.privacy_url %}
<li><a href="{% safelink request.event.settings.privacy_url %}" target="_blank" rel="noopener">{% trans "Privacy policy" %}</a></li>
{% endif %}
{% if request.event.settings.cookie_consent %}
<li><a href="#" id="cookie-consent-reopen">{% trans "Cookie settings" %}</a></li>
{% endif %}
{% if request.event.settings.imprint_url %}
<li><a href="{% safelink request.event.settings.imprint_url %}" target="_blank" rel="noopener">{% trans "Imprint" %}</a></li>
{% endif %}
@@ -14,6 +14,7 @@
<script type="text/javascript" src="{% static "pretixpresale/js/widget/floatformat.js" %}"></script>
<script type="text/javascript" src="{% static "pretixpresale/js/ui/questions.js" %}"></script>
<script type="text/javascript" src="{% static "pretixpresale/js/ui/main.js" %}"></script>
<script type="text/javascript" src="{% static "pretixpresale/js/ui/cookieconsent.js" %}"></script>
<script type="text/javascript" src="{% static "pretixbase/js/asynctask.js" %}"></script>
<script type="text/javascript" src="{% static "pretixpresale/js/ui/cart.js" %}"></script>
<script type="text/javascript" src="{% static "lightbox/js/lightbox.js" %}"></script>
@@ -1,4 +1,6 @@
{% load i18n %}
{% load rich_text %}
{% load safelink %}
<div id="ajaxerr">
</div>
<div id="loadingmodal" hidden aria-live="polite">
@@ -15,3 +17,45 @@
</div>
</div>
</div>
{% if request.organizer %}
<script type="text/plain" id="cookie-consent-storage-key">cookie-consent-{{ request.organizer.slug }}</script>
<div id="cookie-consent-modal" hidden 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">
{% 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 %}
<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">
{{ 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 %}
</div>
</div>
</div>
</div>
{% endif %}
@@ -87,6 +87,12 @@
{% if not request.event and request.organizer.settings.contact_mail %}
<li><a href="mailto:{{ request.organizer.settings.contact_mail }}">{% trans "Contact event organizer" %}</a></li>
{% endif %}
{% if not request.event and request.organizer.settings.privacy_url %}
<li><a href="{% safelink request.organizer.settings.privacy_url %}" target="_blank" rel="noopener">{% trans "Privacy policy" %}</a></li>
{% endif %}
{% if not request.event and request.organizer.settings.cookie_consent %}
<li><a href="#" id="cookie-consent-reopen">{% trans "Cookie settings" %}</a></li>
{% endif %}
{% if not request.event and request.organizer.settings.imprint_url %}
<li><a href="{% safelink request.organizer.settings.imprint_url %}" target="_blank" rel="noopener">{% trans "Imprint" %}</a></li>
{% endif %}
@@ -0,0 +1,39 @@
/*global $ */
window.__pretix_cookie_update_listeners = window.__pretix_cookie_update_listeners || []
$(function () {
var storage_key = $("#cookie-consent-storage-key").text();
if (!window.localStorage[storage_key]) {
$("#cookie-consent-modal").show();
}
$("#cookie-consent-button-yes").on("click", function () {
window.localStorage[storage_key] = JSON.stringify({
'functionality': true,
'ad': true,
'analytics': true,
'social': true,
})
for (var k of window.__pretix_cookie_update_listeners) {
k.call(this, window.localStorage[storage_key])
}
$("#cookie-consent-modal").hide()
})
$("#cookie-consent-button-no").on("click", function () {
window.localStorage[storage_key] = JSON.stringify({
'functionality': true,
'ad': false,
'analytics': false,
'social': false,
})
for (var k of window.__pretix_cookie_update_listeners) {
k.call(this, window.localStorage[storage_key])
}
$("#cookie-consent-modal").hide()
})
$("#cookie-consent-reopen").on("click", function (e) {
$("#cookie-consent-modal").show()
e.preventDefault()
return true
})
});
+15 -3
View File
@@ -136,7 +136,7 @@ body.loading .container {
font-size: 120px;
color: $brand-primary;
}
#loadingmodal, #ajaxerr {
#loadingmodal, #ajaxerr, #cookie-consent-modal {
position: fixed;
top: 0;
left: 0;
@@ -156,9 +156,10 @@ body.loading .container {
.modal-card {
margin: 50px auto 0;
top: 50px;
width: 90%;
max-width: 600px;
max-height: calc(100vh - 100px);
overflow-y: auto;
background: white;
border-radius: $border-radius-large;
box-shadow: 0 7px 14px 0 rgba(78, 50, 92, 0.1),0 3px 6px 0 rgba(0,0,0,.07);
@@ -178,10 +179,21 @@ body.loading .container {
}
}
}
&#cookie-consent-modal {
background: rgba(255, 255, 255, .5);
opacity: 1;
visibility: visible;
.modal-card-content {
margin-left: 0;
}
}
}
@media (max-width: 700px) {
#loadingmodal, #ajaxerr {
#loadingmodal, #ajaxerr, #cookie-consent-modal {
.modal-card {
margin: 25px auto 0;
max-height: calc(100vh - 50px - 20px);
.modal-card-icon {
float: none;
width: 100%;