Compare commits

..

8 Commits

Author SHA1 Message Date
Raphael Michel
5094e5e4ec Bump to 3.14.2 2021-01-12 10:59:16 +01:00
Raphael Michel
222cab115e Fix UX quirk in phone number field triggered by American numbers 2021-01-12 10:58:57 +01:00
Raphael Michel
7809f8ac1a API: Fix CSS generation after change in event settings 2021-01-12 10:58:46 +01:00
Raphael Michel
839c76769c Fix duplicate listing of fonts in event settings 2021-01-12 10:58:16 +01:00
Raphael Michel
8b4cf9db70 Fix tests failing in 2021 2021-01-12 10:58:16 +01:00
Raphael Michel
099ab079f9 Fix geocoding with opencage 2021-01-12 10:58:16 +01:00
Raphael Michel
56c861036c Bump to 3.14.1 2020-12-22 13:19:59 +01:00
Raphael Michel
211fddf308 Fix #1888 -- UnknownLocaleError if locale is set 2020-12-22 13:19:51 +01:00
31 changed files with 126 additions and 220 deletions

View File

@@ -135,7 +135,7 @@ Fill the configuration file ``/etc/pretix/pretix.cfg`` with the following conten
user=pretix
; Replace with the password you chose above
password=*********
; In most docker setups, 172.17.0.1 is the address of the docker host. Adjust
; In most docker setups, 172.17.0.1 is the address of the docker host. Adjuts
; this to wherever your database is running, e.g. the name of a linked container
; or of a mounted MySQL socket.
host=172.17.0.1

View File

@@ -14,9 +14,7 @@ Control panel views
-------------------
If you want to add a custom view to the control area of an event, just register an URL in your
``urls.py`` that lives in the ``/control/`` subpath:
.. code-block:: python
``urls.py`` that lives in the ``/control/`` subpath::
from django.conf.urls import url
@@ -46,9 +44,7 @@ If only the ``organizer`` parameter is present, it will be ensured that:
* The user has permission to access view the current organizer
If you want to require specific permission types, we provide you with a decorator or a mixin for
your views:
.. code-block:: python
your views::
from pretix.control.permissions import (
event_permission_required, EventPermissionRequiredMixin
@@ -65,9 +61,8 @@ your views:
...
Similarly, there is ``organizer_permission_required`` and ``OrganizerPermissionRequiredMixin``. In case of
event-related views, there is also a signal that allows you to add the view to the event navigation like this:
event-related views, there is also a signal that allows you to add the view to the event navigation like this::
.. code-block:: python
from django.urls import resolve, reverse
from django.dispatch import receiver
@@ -95,9 +90,7 @@ Event settings view
-------------------
A special case of a control panel view is a view hooked into the event settings page. For this case, there is a
special navigation signal:
.. code-block:: python
special navigation signal::
@receiver(nav_event_settings, dispatch_uid='friends_tickets_nav_settings')
def navbar_settings(sender, request, **kwargs):
@@ -112,9 +105,7 @@ special navigation signal:
}]
Also, your view should inherit from ``EventSettingsViewMixin`` and your template from ``pretixcontrol/event/settings_base.html``
for good integration. If you just want to display a form, you could do it like the following:
.. code-block:: python
for good integration. If you just want to display a form, you could do it like the following::
class MySettingsView(EventSettingsViewMixin, EventSettingsFormView):
model = Event
@@ -156,9 +147,7 @@ Including a custom view into the participant-facing frontend is a little bit dif
no path prefix like ``control/``.
First, define your URL in your ``urls.py``, but this time in the ``event_patterns`` section and wrapped by
``event_url``:
.. code-block:: python
``event_url``::
from pretix.multidomain import event_url
@@ -193,9 +182,8 @@ standard Django request handling: There are `ViewSets`_ to group related views i
automatically build URL configurations from them.
To integrate a custom viewset with pretix' REST API, you can just register with one of our routers within the
``urls.py`` module of your plugin:
``urls.py`` module of your plugin::
.. code-block:: python
from pretix.api.urls import event_router, router, orga_router
@@ -212,9 +200,7 @@ in the control panel. However, you need to make sure on your own only to return
.event`` and ``request.organizer`` are available as usual.
To require a special permission like ``can_view_orders``, you do not need to inherit from a special ViewSet base
class, you can just set the ``permission`` attribute on your viewset:
.. code-block:: python
class, you can just set the ``permission`` attribute on your viewset::
class MyViewSet(ModelViewSet):
permission = 'can_view_orders'
@@ -222,9 +208,8 @@ class, you can just set the ``permission`` attribute on your viewset:
If you want to check the permission only for some methods of your viewset, you have to do it yourself. Note here that
API authentications can be done via user sessions or API tokens and you should therefore check something like the
following:
following::
.. code-block:: python
perm_holder = (request.auth if isinstance(request.auth, TeamAPIToken) else request.user)
if perm_holder.has_event_permission(request.event.organizer, request.event, 'can_view_orders'):

View File

@@ -15,9 +15,7 @@ Output registration
The email HTML renderer API does not make a lot of usage from signals, however, it
does use a signal to get a list of all available email renderers. Your plugin
should listen for this signal and return the subclass of ``pretix.base.email.BaseHTMLMailRenderer``
that we'll provide in this plugin:
.. code-block:: python
that we'll provide in this plugin::
from django.dispatch import receiver
@@ -74,9 +72,7 @@ class ``TemplateBasedMailRenderer`` that you can re-use to perform the following
* Call `inlinestyler`_ to convert all ``<style>`` style sheets to inline ``style=""``
attributes for better compatibility
To use it, you just need to implement some variables:
.. code-block:: python
To use it, you just need to implement some variables::
class ClassicMailRenderer(TemplateBasedMailRenderer):
verbose_name = _('pretix default')

View File

@@ -17,9 +17,7 @@ Exporter registration
The exporter API does not make a lot of usage from signals, however, it does use a signal to get a list of
all available exporters. Your plugin should listen for this signal and return the subclass of
``pretix.base.exporter.BaseExporter``
that we'll provide in this plugin:
.. code-block:: python
that we'll provide in this plugin::
from django.dispatch import receiver
@@ -33,9 +31,7 @@ that we'll provide in this plugin:
Some exporters might also prove to be useful, when provided on an organizer-level. In order to declare your
exporter as capable of providing exports spanning multiple events, your plugin should listen for this signal
and return the subclass of ``pretix.base.exporter.BaseExporter`` that we'll provide in this plugin:
.. code-block:: python
and return the subclass of ``pretix.base.exporter.BaseExporter`` that we'll provide in this plugin::
from django.dispatch import receiver

View File

@@ -15,9 +15,7 @@ Output registration
The invoice renderer API does not make a lot of usage from signals, however, it
does use a signal to get a list of all available invoice renderers. Your plugin
should listen for this signal and return the subclass of ``pretix.base.invoice.BaseInvoiceRenderer``
that we'll provide in this plugin:
.. code-block:: python
that we'll provide in this plugin::
from django.dispatch import receiver

View File

@@ -19,9 +19,7 @@ Provider registration
The payment provider API does not make a lot of usage from signals, however, it
does use a signal to get a list of all available payment providers. Your plugin
should listen for this signal and return the subclass of ``pretix.base.payment.BasePaymentProvider``
that the plugin will provide:
.. code-block:: python
that the plugin will provide::
from django.dispatch import receiver
@@ -142,9 +140,7 @@ it is necessary to introduce additional views. One example is the PayPal
provider. It redirects the user to a PayPal website in the
:py:meth:`BasePaymentProvider.checkout_prepare` step of the checkout process
and provides PayPal with a URL to redirect back to. This URL points to a
view which looks roughly like this:
.. code-block:: python
view which looks roughly like this::
@login_required
def success(request):

View File

@@ -13,9 +13,7 @@ Placeholder registration
The placeholder API does not make a lot of usage from signals, however, it
does use a signal to get a list of all available email placeholders. Your plugin
should listen for this signal and return an instance of a subclass of ``pretix.base.email.BaseMailTextPlaceholder``:
.. code-block:: python
should listen for this signal and return an instance of a subclass of ``pretix.base.email.BaseMailTextPlaceholder``::
from django.dispatch import receiver
@@ -73,9 +71,7 @@ Helper class for simple placeholders
------------------------------------
pretix ships with a helper class that makes it easy to provide placeholders based on simple
functions:
.. code-block:: python
functions::
placeholder = SimpleFunctionalMailTextPlaceholder(
'code', ['order'], lambda order: order.code, sample='F8VVL'

View File

@@ -55,9 +55,7 @@ restricted boolean (optional) ``False`` by default, restricts a plugin
compatibility string Specifier for compatible pretix versions.
================== ==================== ===========================================================
A working example would be:
.. code-block:: python
A working example would be::
try:
from pretix.base.plugins import PluginConfig
@@ -83,7 +81,7 @@ A working example would be:
default_app_config = 'pretix_paypal.PaypalApp'
The ``AppConfig`` class may implement a property ``compatibility_errors``, that checks
The ``AppConfig`` class may implement a property ``compatiblity_errors``, that checks
whether the pretix installation meets all requirements of the plugin. If so,
it should contain ``None`` or an empty list, otherwise a list of strings containing
human-readable error messages. We recommend using the ``django.utils.functional.cached_property``
@@ -98,9 +96,7 @@ Plugin registration
Somehow, pretix needs to know that your plugin exists at all. For this purpose, we
make use of the `entry point`_ feature of setuptools. To register a plugin that lives
in a separate python package, your ``setup.py`` should contain something like this:
.. code-block:: python
in a separate python package, your ``setup.py`` should contain something like this::
setup(
args...,
@@ -122,9 +118,7 @@ The various components of pretix define a number of signals which your plugin ca
listen for. We will go into the details of the different signals in the following
pages. We suggest that you put your signal receivers into a ``signals`` submodule
of your plugin. You should extend your ``AppConfig`` (see above) by the following
method to make your receivers available:
.. code-block:: python
method to make your receivers available::
class PaypalApp(AppConfig):
@@ -133,9 +127,7 @@ method to make your receivers available:
from . import signals # NOQA
You can optionally specify code that is executed when your plugin is activated for an event
in the ``installed`` method:
.. code-block:: python
in the ``installed`` method::
class PaypalApp(AppConfig):

View File

@@ -74,7 +74,7 @@ looks like this:
def generate_files(self) -> List[Tuple[str, str, str]]:
yield 'invoice-addresses.json', 'application/json', json.dumps({
ia.order.code: InvoiceAddressSerializer(ia).data
ia.order.code: InvoiceAdddressSerializer(ia).data
for ia in InvoiceAddress.objects.filter(order__event=self.event)
}, indent=4)

View File

@@ -17,9 +17,7 @@ Output registration
The ticket output API does not make a lot of usage from signals, however, it
does use a signal to get a list of all available ticket outputs. Your plugin
should listen for this signal and return the subclass of ``pretix.base.ticketoutput.BaseTicketOutput``
that we'll provide in this plugin:
.. code-block:: python
that we'll provide in this plugin::
from django.dispatch import receiver

View File

@@ -12,9 +12,7 @@ Implementing a task
-------------------
A common pattern for implementing asynchronous tasks can be seen a lot in ``pretix.base.services``
and looks like this:
.. code-block:: python
and looks like this::
from pretix.celery_app import app
@@ -36,15 +34,13 @@ If your user needs to wait for the response of the asynchronous task, there are
that will probably move to ``pretix.base`` at some point. They consist of the view mixin ``AsyncAction`` that allows
you to easily write a view that kicks off and waits for an asynchronous task. ``AsyncAction`` will determine whether
to run the task asynchronously or not and will do some magic to look nice for users with and without JavaScript support.
A usage example taken directly from the code is:
.. code-block:: python
A usage example taken directly from the code is::
class OrderCancelDo(EventViewMixin, OrderDetailMixin, AsyncAction, View):
"""
A view that executes a task asynchronously. A POST request will kick off the
task into the background or run it in the foreground if celery is not installed.
In the former case, subsequent GET calls can be used to determine the current
In the former case, subsequent GET calls can be used to determinine the current
status of the task.
"""
@@ -83,9 +79,7 @@ A usage example taken directly from the code is:
return super().get_error_message(exception)
On the client side, this can be used by simply adding a ``data-asynctask`` attribute to an HTML form. This will enable
AJAX sending of the form and display a loading indicator:
.. code-block:: html
AJAX sending of the form and display a loading indicator::
<form method="post" data-asynctask
action="{% eventurl request.event "presale:event.order.cancel.do" … %}">

View File

@@ -27,9 +27,7 @@ numbers and dates, ``LazyDate`` and ``LazyNumber``. There also is a ``LazyLocale
exceptions with gettext-localized exception messages.
Last, but definitely not least, we have the ``language`` context manager (``pretix.base.i18n.language``) that allows
you to execute a piece of code with a different locale:
.. code-block:: python
you to execute a piece of code with a different locale::
with language('de'):
render_mail_template()

View File

@@ -16,9 +16,7 @@ We recommend all relevant models to inherit from ``LoggedModel`` as it simplifie
.. autoclass:: pretix.base.models.LoggedModel
:members: log_action, all_logentries
To actually log an action, you can just call the ``log_action`` method on your object:
.. code-block:: python
To actually log an action, you can just call the ``log_action`` method on your object::
order.log_action('pretix.event.order.canceled', user=user, data={})
@@ -31,9 +29,7 @@ Logging form actions
""""""""""""""""""""
A very common use case is to log the changes to a model that have been done in a ``ModelForm``. In this case,
we generally use a custom ``form_valid`` method on our ``FormView`` that looks like this:
.. code-block:: python
we generally use a custom ``form_valid`` method on our ``FormView`` that looks like this::
@transaction.atomic
def form_valid(self, form):
@@ -44,9 +40,7 @@ we generally use a custom ``form_valid`` method on our ``FormView`` that looks l
messages.success(self.request, _('Your changes have been saved.'))
return super().form_valid(form)
It gets a little bit more complicated if your form allows file uploads:
.. code-block:: python
It gets a little bit more complicated if your form allows file uploads::
@transaction.atomic
def form_valid(self, form):
@@ -73,9 +67,7 @@ following ready-to-include template::
We now need a way to translate the action codes like ``pretix.event.changed`` into human-readable
strings. The :py:attr:`pretix.base.signals.logentry_display` signals allows you to do so. A simple
implementation could look like:
.. code-block:: python
implementation could look like::
from django.utils.translation import gettext as _
from pretix.base.signals import logentry_display
@@ -96,9 +88,7 @@ Sending notifications
If you think that the logged information might be important or urgent enough to send out a notification to interested
organizers. In this case, you should listen for the :py:attr:`pretix.base.signals.register_notification_types` signal
to register a notification type:
.. code-block:: python
to register a notification type::
@receiver(register_notification_types)
def register_my_notification_types(sender, **kwargs):
@@ -113,9 +103,7 @@ You should subclass the base ``NotificationType`` class and implement all its me
.. autoclass:: pretix.base.notifications.NotificationType
:members: action_type, verbose_name, required_permission, build_notification
A simple implementation could look like this:
.. code-block:: python
A simple implementation could look like this::
class MyNotificationType(NotificationType):
required_permission = "can_view_orders"
@@ -155,9 +143,7 @@ Logging technical information
-----------------------------
If you just want to log technical information to a log file on disk that does not need to be parsed
and displayed later, you can just use Python's ``logging`` module:
.. code-block:: python
and displayed later, you can just use Python's ``logging`` module::
import logging
@@ -165,9 +151,7 @@ and displayed later, you can just use Python's ``logging`` module:
logger.info('Startup complete.')
This is also very useful to provide debugging information when an exception occurs:
.. code-block:: python
This is also very useful to provide debugging information when an exception occurs::
try:
foo()

View File

@@ -15,9 +15,7 @@ Requiring permissions for a view
--------------------------------
pretix provides a number of useful mixins and decorators that allow you to specify that a user needs a certain
permission level to access a view:
.. code-block:: python
permission level to access a view::
from pretix.control.permissions import (
OrganizerPermissionRequiredMixin, organizer_permission_required
@@ -46,9 +44,7 @@ permission level to access a view:
# Only users with *any* permission on this organizer can access this
Of course, the same is available on event level:
.. code-block:: python
Of course, the same is available on event level::
from pretix.control.permissions import (
EventPermissionRequiredMixin, event_permission_required
@@ -77,9 +73,7 @@ Of course, the same is available on event level:
# Only users with *any* permission on this event can access this
You can also require that this view is only accessible by system administrators with an active "admin session"
(see below for what this means):
.. code-block:: python
(see below for what this means)::
from pretix.control.permissions import (
AdministratorPermissionRequiredMixin, administrator_permission_required
@@ -95,9 +89,7 @@ You can also require that this view is only accessible by system administrators
# ...
In rare cases it might also be useful to expose a feature only to people who have a staff account but do not
necessarily have an active admin session:
.. code-block:: python
necessarily have an active admin session::
from pretix.control.permissions import (
StaffMemberRequiredMixin, staff_member_required

View File

@@ -39,9 +39,7 @@ subclass that also adds support for internationalized fields:
.. autoclass:: pretix.base.forms.SettingsForm
You can simply use it like this:
.. code-block:: python
You can simply use it like this::
class EventSettingsForm(SettingsForm):
show_date_to = forms.BooleanField(
@@ -58,9 +56,7 @@ You can simply use it like this:
Defaults in plugins
-------------------
Plugins can add custom hardcoded defaults in the following way:
.. code-block:: python
Plugins can add custom hardcoded defaults in the following way::
from pretix.base.settings import settings_hierarkey

View File

@@ -1 +1 @@
__version__ = "3.15.0.dev0"
__version__ = "3.14.2"

View File

@@ -392,6 +392,6 @@ class EventSettingsView(views.APIView):
}
)
if any(p in s.changed_data for p in SETTINGS_AFFECTING_CSS):
regenerate_css.apply_async(args=(request.organizer.pk,))
regenerate_css.apply_async(args=(request.event.pk,))
s = EventSettingsSerializer(instance=request.event.settings, event=request.event)
return Response(s.data)

View File

@@ -9,12 +9,14 @@ import pycountry
import pytz
import vat_moss.errors
import vat_moss.id
from babel import Locale
from django import forms
from django.contrib import messages
from django.core.exceptions import ValidationError
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db.models import QuerySet
from django.forms import Select
from django.utils import translation
from django.utils.formats import date_format
from django.utils.html import escape
from django.utils.safestring import mark_safe
@@ -24,9 +26,7 @@ from django_countries import countries
from django_countries.fields import Country, CountryField
from phonenumber_field.formfields import PhoneNumberField
from phonenumber_field.phonenumber import PhoneNumber
from phonenumber_field.widgets import (
PhoneNumberPrefixWidget, PhonePrefixSelect,
)
from phonenumber_field.widgets import PhoneNumberPrefixWidget
from phonenumbers import NumberParseException, national_significant_number
from phonenumbers.data import _COUNTRY_CODE_TO_REGION_CODE
@@ -205,10 +205,39 @@ class NamePartsFormField(forms.MultiValueField):
return value
class WrappedPhonePrefixSelect(PhonePrefixSelect):
def __init__(self, *args, **kwargs):
with language(get_babel_locale()):
super().__init__(*args, **kwargs)
class WrappedPhonePrefixSelect(Select):
initial = None
def __init__(self, initial=None):
choices = [("", "---------")]
language = get_babel_locale() # changed from default implementation that used the django locale
locale = Locale(translation.to_locale(language))
for prefix, values in _COUNTRY_CODE_TO_REGION_CODE.items():
prefix = "+%d" % prefix
if initial and initial in values:
self.initial = prefix
for country_code in values:
country_name = locale.territories.get(country_code)
if country_name:
choices.append((prefix, "{} {}".format(country_name, prefix)))
super().__init__(choices=sorted(choices, key=lambda item: item[1]))
def render(self, name, value, *args, **kwargs):
return super().render(name, value or self.initial, *args, **kwargs)
def get_context(self, name, value, attrs):
if value and self.choices[1][0] != value:
matching_choices = len([1 for p, c in self.choices if p == value])
if matching_choices > 1:
# Some countries share a phone pretix, for example +1 is used all over the Americas.
# This causes a UX problem: If the default value or the existing data is +12125552368,
# the widget will just show the first <option> entry with value="+1" as selected,
# which alphabetically is America Samoa, although most numbers statistically are from
# the US. As a workaround, we detect this case and add an aditional choice value with
# just <option value="+1">+1</option> without an explicit country.
self.choices.insert(1, (value, value))
context = super().get_context(name, value, attrs)
return context
class WrappedPhoneNumberPrefixWidget(PhoneNumberPrefixWidget):

View File

@@ -35,7 +35,6 @@ from pretix.helpers.countries import CachedCountries
from pretix.multidomain.models import KnownDomain
from pretix.multidomain.urlreverse import build_absolute_uri
from pretix.plugins.banktransfer.payment import BankTransfer
from pretix.presale.style import get_fonts
class EventWizardFoundationForm(forms.Form):
@@ -549,9 +548,6 @@ class EventSettingsForm(SettingsForm):
if not self.event.has_subevents:
del self.fields['frontpage_subevent_ordering']
del self.fields['event_list_type']
self.fields['primary_font'].choices += [
(a, {"title": a, "data": v}) for a, v in get_fonts().items()
]
# create "virtual" fields for better UX when editing <name>_asked and <name>_required fields
self.virtual_keys = []

View File

@@ -292,8 +292,6 @@ def pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs):
'pretix.event.order.denied': _('The order has been denied.'),
'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.locale.changed': _('The order locale has been changed.'),

View File

@@ -1,5 +1,4 @@
from django.conf.urls import include, url
from django.views.generic.base import RedirectView
from pretix.control.views import (
auth, checkin, dashboards, event, geo, global_settings, item, main, oauth,
@@ -304,5 +303,4 @@ urlpatterns = [
url(r'^checkinlists/(?P<list>\d+)/delete$', checkin.CheckinListDelete.as_view(),
name='event.orders.checkinlists.delete'),
])),
url(r'^event/(?P<organizer>[^/]+)/$', RedirectView.as_view(pattern_name='control:organizer'), name='event.organizerredirect'),
]

View File

@@ -25,7 +25,7 @@ class GeoCodeView(LoginRequiredMixin, View):
gs = GlobalSettingsObject()
if gs.settings.opencagedata_apikey:
res = self._use_opencage(q)
if gs.settings.mapquest_apikey:
elif gs.settings.mapquest_apikey:
res = self._use_mapquest(q)
else:
return JsonResponse({
@@ -71,7 +71,7 @@ class GeoCodeView(LoginRequiredMixin, View):
try:
r = requests.get(
'https://www.mapquestapi.com/geocoding/v1/address?location={}&key={}'.format(
'http://www.mapquestapi.com/geocoding/v1/address?location={}&key={}'.format(
quote(q), gs.settings.mapquest_apikey
)
)

View File

@@ -7,10 +7,10 @@ msgstr ""
"Project-Id-Version: 1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-12-22 11:05+0000\n"
"PO-Revision-Date: 2020-12-23 04:00+0000\n"
"PO-Revision-Date: 2020-12-22 06:00+0000\n"
"Last-Translator: Maarten van den Berg <maartenberg1@gmail.com>\n"
"Language-Team: Dutch <https://translate.pretix.eu/projects/pretix/pretix/nl/>"
"\n"
"Language-Team: Dutch <https://translate.pretix.eu/projects/pretix/pretix/nl/"
">\n"
"Language: nl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -1484,8 +1484,6 @@ msgstr ""
#: pretix/base/forms/auth.py:26 pretix/base/forms/user.py:23
msgid "For security reasons, please wait 5 minutes before you try again."
msgstr ""
"Om veiligheidsredenen moet u vijf minuten wachten voor u dit opnieuw kunt "
"proberen."
#: pretix/base/forms/auth.py:27 pretix/base/forms/auth.py:223
msgid "This account is inactive."
@@ -8157,16 +8155,18 @@ msgid "Free text input"
msgstr "Vrije tekstinvoer"
#: pretix/control/forms/event.py:575
#, fuzzy
#| msgid "Do not copy"
msgid "Do not ask"
msgstr "Niet vragen"
msgstr "Niet kopiëren"
#: pretix/control/forms/event.py:576
msgid "Ask, but do not require input"
msgstr "Vraag, maar maak invoer niet verplicht"
msgstr ""
#: pretix/control/forms/event.py:577
msgid "Ask and require input"
msgstr "Vraag, invullen is niet verplicht"
msgstr ""
#: pretix/control/forms/event.py:612
msgid ""
@@ -8616,12 +8616,16 @@ msgid "Cancellations"
msgstr "Annuleringen"
#: pretix/control/forms/filter.py:153
#, fuzzy
#| msgid "Canceled (paid fee)"
msgid "Canceled (fully)"
msgstr "Geannuleerd (hele bestelling)"
msgstr "Geannuleerd (betaalde kosten)"
#: pretix/control/forms/filter.py:154
#, fuzzy
#| msgid "Canceled (or with paid fee)"
msgid "Canceled (fully or with paid fee)"
msgstr "Geannuleerd (hele bestelling of met betaalde kosten)"
msgstr "Geannuleerd (of met betaalde kosten)"
#: pretix/control/forms/filter.py:155
msgid "Cancellation requested"
@@ -8657,12 +8661,17 @@ msgid "Overpaid"
msgstr "Overbetaald"
#: pretix/control/forms/filter.py:162
#, fuzzy
#| msgid "Mark as paid"
msgid "Partially paid"
msgstr "Gedeeltelijk betaald"
msgstr "Markeren als betaald"
#: pretix/control/forms/filter.py:163
#, fuzzy
#| msgctxt "checkoutflow"
#| msgid "Order confirmed"
msgid "Underpaid (but confirmed)"
msgstr "Onderbetaald (maar bevestigd)"
msgstr "Bestelling bevestigd"
#: pretix/control/forms/filter.py:164
msgid "Pending (but fully paid)"
@@ -15132,8 +15141,10 @@ msgid "Remove filter"
msgstr "Verwijder filter"
#: pretix/control/templates/pretixcontrol/orders/index.html:116
#, fuzzy
#| msgid "Order total"
msgid "Order paid / total"
msgstr "Bestelling betaald / totaal"
msgstr "Totaalbedrag van bestelling"
#: pretix/control/templates/pretixcontrol/orders/index.html:151
#: pretix/control/templates/pretixcontrol/search/orders.html:83
@@ -18787,7 +18798,7 @@ msgstr "De bestelling is al geannuleerd."
#: pretix/plugins/banktransfer/tasks.py:117
msgid "Automatic split to multiple orders not possible."
msgstr "Automatisch uitsplitsen naar meerdere bestellingen niet mogelijk."
msgstr ""
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/checkout_payment_form.html:4
msgid ""
@@ -18816,8 +18827,11 @@ msgid "Account"
msgstr "Rekening"
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/control.html:22
#, fuzzy
#| msgctxt "amount_label"
#| msgid "Transfer"
msgid "Transfer amount"
msgstr "Overschrijvingsbedrag"
msgstr "Overdragen"
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/control.html:26
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/import_assign.html:33

View File

@@ -273,7 +273,7 @@ class AddOnsStep(CartMixin, AsyncAction, TemplateFlowStep):
net=a.price - a.tax_value,
gross=a.price,
tax=a.tax_value,
name=a.item.tax_rule.name if a.item.tax_rule else "",
name=a.item.tax_rule.name,
rate=a.tax_rate,
)
else:

View File

@@ -1,8 +1,6 @@
from itertools import chain
import dns.resolver
from django import forms
from django.core.cache import cache
from django.core.exceptions import ValidationError
from django.utils.encoding import force_str
from django.utils.translation import gettext_lazy as _
@@ -20,52 +18,12 @@ from pretix.base.validators import EmailBanlistValidator
from pretix.presale.signals import contact_form_fields
class EmailDNSValidator:
msg = _('Please check your email domain, it does not look like "%(value)s" is able to receive emails.')
def __call__(self, value):
domain = value.split('@')[-1]
works = cache.get(f"mail_domain_exists_{domain}")
if works == "true":
return value
if works == "false":
raise ValidationError(self.msg, code='dns', params={'value': domain})
resolver = dns.resolver.Resolver()
resolver.lifetime = 0.5
resolver.timeout = 0.5
record_types = ('MX', 'AAAA', 'A')
for record_type in record_types:
try:
if len(resolver.query(domain, record_type)):
cache.set(f"mail_domain_exists_{domain}", "true", 3600 * 24 * 7)
return value
except (dns.resolver.NoAnswer, dns.resolver.NXDOMAIN, dns.resolver.YXDOMAIN):
# These errors represent a non-existing domain or entry, we want to continue with the
# other types of records and if all fail, we'll report an error.
continue
except:
# Timeout, IO errors, etc.: We don't know what's happening! Let's not stop the user
# from buying and just not validate this. Let's cache this result for a short while
# to prevent issues during peak load
cache.set(f"mail_domain_exists_{domain}", "false", 60)
return value
# No valid record found, even though the requests did not fail. This domain will not receive email.
# Cache this result for a short time.
cache.set(f"mail_domain_exists_{domain}", "false", 300)
raise ValidationError(self.msg, code='dns', params={'value': domain})
class ContactForm(forms.Form):
required_css_class = 'required'
email = forms.EmailField(
label=_('E-mail'),
validators=[
EmailBanlistValidator(),
EmailDNSValidator(),
],
widget=forms.EmailInput(attrs={'autocomplete': 'section-contact email'})
)
email = forms.EmailField(label=_('E-mail'),
validators=[EmailBanlistValidator()],
widget=forms.EmailInput(attrs={'autocomplete': 'section-contact email'})
)
def __init__(self, *args, **kwargs):
self.event = kwargs.pop('event')

View File

@@ -183,7 +183,7 @@
{% endif %}
</div>
{% endif %}
{% if not cart_namespace or subevent %}
{% if not cart_namespace %}
<div>
{% if ev.location %}
<div class="info-row">

View File

@@ -120,10 +120,8 @@ class OrderOpen(EventViewMixin, OrderDetailMixin, View):
if not self.order:
raise Http404(_('Unknown order code or not authorized to access this order.'))
if kwargs.get('hash') == self.order.email_confirm_hash():
if not self.order.email_known_to_work:
self.order.log_action('pretix.event.order.contact.confirmed')
self.order.email_known_to_work = True
self.order.save(update_fields=['email_known_to_work'])
self.order.email_known_to_work = True
self.order.save(update_fields=['email_known_to_work'])
return redirect(self.get_order_url())

View File

@@ -44,7 +44,6 @@ oauthlib==3.1.*
django-jsonfallback>=2.1.2
psycopg2-binary
tqdm==4.*
dnspython==1.16.*
# Stripe
stripe==2.42.*
# PayPal

View File

@@ -134,7 +134,6 @@ setup(
'django-i18nfield>=1.7.0',
'django-jsonfallback>=2.1.2',
'psycopg2-binary',
'dnspython==1.16.*',
'tqdm==4.*',
'vobject==0.9.*',
'pycountry',

View File

@@ -55,10 +55,6 @@ class EventsTest(SoupTest):
self.assertNotIn("31C3", tabletext)
self.assertNotIn("MRMCD14", tabletext)
def test_convenience_organizer_redirect(self):
resp = self.client.get('/control/event/%s/' % (self.orga1.slug))
self.assertRedirects(resp, '/control/organizer/%s/' % (self.orga1.slug))
def test_quick_setup_later(self):
with scopes_disabled():
self.event1.quotas.create(name='foo', size=2)

View File

@@ -142,7 +142,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
data = json.loads(response.content.decode())
assert data == {
"name": "30C3",
"date_range": "Dec. 26, 2021 00:00",
"date_range": f"Dec. 26, {self.event.date_from.year} 00:00",
"currency": "EUR",
"show_variations_expanded": False,
"display_net_prices": False,
@@ -276,7 +276,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
data = json.loads(response.content.decode())
assert data == {
"name": "30C3",
"date_range": "Dec. 26, 2021 00:00",
"date_range": f"Dec. 26, {self.event.date_from.year} 00:00",
"currency": "EUR",
"show_variations_expanded": False,
"display_net_prices": False,
@@ -327,7 +327,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
data = json.loads(response.content.decode())
assert data == {
"name": "30C3",
"date_range": "Dec. 26, 2021 00:00",
"date_range": f"Dec. 26, {self.event.date_from.year} 00:00",
"currency": "EUR",
"show_variations_expanded": False,
"display_net_prices": False,
@@ -394,7 +394,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
data = json.loads(response.content.decode())
assert data == {
"name": "30C3",
"date_range": "Dec. 26, 2021 00:00",
"date_range": f"Dec. 26, {self.event.date_from.year} 00:00",
"currency": "EUR",
'poweredby': '<a href="https://pretix.eu" target="_blank" rel="noopener">event ticketing powered by pretix</a>',
"show_variations_expanded": False,