forked from CGM_Public/pretix_original
Compare commits
13 Commits
v3.14.1
...
validate-d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe5a2286a4 | ||
|
|
3d66bfee7f | ||
|
|
0b495a4070 | ||
|
|
23aba9b5ef | ||
|
|
454f0f6fc8 | ||
|
|
002ff38fba | ||
|
|
dc8bd59715 | ||
|
|
56a2da08df | ||
|
|
4762d6818f | ||
|
|
e99e91d20f | ||
|
|
9fee2d0fbc | ||
|
|
3f30ddc9ab | ||
|
|
641a848f30 |
@@ -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. Adjuts
|
||||
; In most docker setups, 172.17.0.1 is the address of the docker host. Adjust
|
||||
; 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
|
||||
|
||||
@@ -14,7 +14,9 @@ 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::
|
||||
``urls.py`` that lives in the ``/control/`` subpath:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
@@ -44,7 +46,9 @@ 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::
|
||||
your views:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pretix.control.permissions import (
|
||||
event_permission_required, EventPermissionRequiredMixin
|
||||
@@ -61,8 +65,9 @@ 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
|
||||
@@ -90,7 +95,9 @@ 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::
|
||||
special navigation signal:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@receiver(nav_event_settings, dispatch_uid='friends_tickets_nav_settings')
|
||||
def navbar_settings(sender, request, **kwargs):
|
||||
@@ -105,7 +112,9 @@ 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::
|
||||
for good integration. If you just want to display a form, you could do it like the following:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class MySettingsView(EventSettingsViewMixin, EventSettingsFormView):
|
||||
model = Event
|
||||
@@ -147,7 +156,9 @@ 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``::
|
||||
``event_url``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pretix.multidomain import event_url
|
||||
|
||||
@@ -182,8 +193,9 @@ 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
|
||||
|
||||
@@ -200,7 +212,9 @@ 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::
|
||||
class, you can just set the ``permission`` attribute on your viewset:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class MyViewSet(ModelViewSet):
|
||||
permission = 'can_view_orders'
|
||||
@@ -208,8 +222,9 @@ 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'):
|
||||
|
||||
@@ -15,7 +15,9 @@ 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::
|
||||
that we'll provide in this plugin:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from django.dispatch import receiver
|
||||
|
||||
@@ -72,7 +74,9 @@ 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::
|
||||
To use it, you just need to implement some variables:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class ClassicMailRenderer(TemplateBasedMailRenderer):
|
||||
verbose_name = _('pretix default')
|
||||
|
||||
@@ -17,7 +17,9 @@ 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::
|
||||
that we'll provide in this plugin:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from django.dispatch import receiver
|
||||
|
||||
@@ -31,7 +33,9 @@ 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::
|
||||
and return the subclass of ``pretix.base.exporter.BaseExporter`` that we'll provide in this plugin:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from django.dispatch import receiver
|
||||
|
||||
|
||||
@@ -15,7 +15,9 @@ 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::
|
||||
that we'll provide in this plugin:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from django.dispatch import receiver
|
||||
|
||||
|
||||
@@ -19,7 +19,9 @@ 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::
|
||||
that the plugin will provide:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from django.dispatch import receiver
|
||||
|
||||
@@ -140,7 +142,9 @@ 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::
|
||||
view which looks roughly like this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@login_required
|
||||
def success(request):
|
||||
|
||||
@@ -13,7 +13,9 @@ 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``::
|
||||
should listen for this signal and return an instance of a subclass of ``pretix.base.email.BaseMailTextPlaceholder``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from django.dispatch import receiver
|
||||
|
||||
@@ -71,7 +73,9 @@ Helper class for simple placeholders
|
||||
------------------------------------
|
||||
|
||||
pretix ships with a helper class that makes it easy to provide placeholders based on simple
|
||||
functions::
|
||||
functions:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
placeholder = SimpleFunctionalMailTextPlaceholder(
|
||||
'code', ['order'], lambda order: order.code, sample='F8VVL'
|
||||
|
||||
@@ -55,7 +55,9 @@ restricted boolean (optional) ``False`` by default, restricts a plugin
|
||||
compatibility string Specifier for compatible pretix versions.
|
||||
================== ==================== ===========================================================
|
||||
|
||||
A working example would be::
|
||||
A working example would be:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
try:
|
||||
from pretix.base.plugins import PluginConfig
|
||||
@@ -81,7 +83,7 @@ A working example would be::
|
||||
|
||||
default_app_config = 'pretix_paypal.PaypalApp'
|
||||
|
||||
The ``AppConfig`` class may implement a property ``compatiblity_errors``, that checks
|
||||
The ``AppConfig`` class may implement a property ``compatibility_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``
|
||||
@@ -96,7 +98,9 @@ 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::
|
||||
in a separate python package, your ``setup.py`` should contain something like this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
setup(
|
||||
args...,
|
||||
@@ -118,7 +122,9 @@ 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::
|
||||
method to make your receivers available:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class PaypalApp(AppConfig):
|
||||
…
|
||||
@@ -127,7 +133,9 @@ 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::
|
||||
in the ``installed`` method:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class PaypalApp(AppConfig):
|
||||
…
|
||||
|
||||
@@ -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: InvoiceAdddressSerializer(ia).data
|
||||
ia.order.code: InvoiceAddressSerializer(ia).data
|
||||
for ia in InvoiceAddress.objects.filter(order__event=self.event)
|
||||
}, indent=4)
|
||||
|
||||
|
||||
@@ -17,7 +17,9 @@ 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::
|
||||
that we'll provide in this plugin:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from django.dispatch import receiver
|
||||
|
||||
|
||||
@@ -12,7 +12,9 @@ Implementing a task
|
||||
-------------------
|
||||
|
||||
A common pattern for implementing asynchronous tasks can be seen a lot in ``pretix.base.services``
|
||||
and looks like this::
|
||||
and looks like this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pretix.celery_app import app
|
||||
|
||||
@@ -34,13 +36,15 @@ 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::
|
||||
A usage example taken directly from the code is:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
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 determinine the current
|
||||
In the former case, subsequent GET calls can be used to determine the current
|
||||
status of the task.
|
||||
"""
|
||||
|
||||
@@ -79,7 +83,9 @@ 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::
|
||||
AJAX sending of the form and display a loading indicator:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<form method="post" data-asynctask
|
||||
action="{% eventurl request.event "presale:event.order.cancel.do" … %}">
|
||||
|
||||
@@ -27,7 +27,9 @@ 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::
|
||||
you to execute a piece of code with a different locale:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
with language('de'):
|
||||
render_mail_template()
|
||||
|
||||
@@ -16,7 +16,9 @@ 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::
|
||||
To actually log an action, you can just call the ``log_action`` method on your object:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
order.log_action('pretix.event.order.canceled', user=user, data={})
|
||||
|
||||
@@ -29,7 +31,9 @@ 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::
|
||||
we generally use a custom ``form_valid`` method on our ``FormView`` that looks like this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@transaction.atomic
|
||||
def form_valid(self, form):
|
||||
@@ -40,7 +44,9 @@ 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::
|
||||
It gets a little bit more complicated if your form allows file uploads:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@transaction.atomic
|
||||
def form_valid(self, form):
|
||||
@@ -67,7 +73,9 @@ 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::
|
||||
implementation could look like:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from django.utils.translation import gettext as _
|
||||
from pretix.base.signals import logentry_display
|
||||
@@ -88,7 +96,9 @@ 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::
|
||||
to register a notification type:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@receiver(register_notification_types)
|
||||
def register_my_notification_types(sender, **kwargs):
|
||||
@@ -103,7 +113,9 @@ 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::
|
||||
A simple implementation could look like this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class MyNotificationType(NotificationType):
|
||||
required_permission = "can_view_orders"
|
||||
@@ -143,7 +155,9 @@ 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::
|
||||
and displayed later, you can just use Python's ``logging`` module:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import logging
|
||||
|
||||
@@ -151,7 +165,9 @@ 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::
|
||||
This is also very useful to provide debugging information when an exception occurs:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
try:
|
||||
foo()
|
||||
|
||||
@@ -15,7 +15,9 @@ 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::
|
||||
permission level to access a view:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pretix.control.permissions import (
|
||||
OrganizerPermissionRequiredMixin, organizer_permission_required
|
||||
@@ -44,7 +46,9 @@ 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::
|
||||
Of course, the same is available on event level:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pretix.control.permissions import (
|
||||
EventPermissionRequiredMixin, event_permission_required
|
||||
@@ -73,7 +77,9 @@ 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)::
|
||||
(see below for what this means):
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pretix.control.permissions import (
|
||||
AdministratorPermissionRequiredMixin, administrator_permission_required
|
||||
@@ -89,7 +95,9 @@ 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::
|
||||
necessarily have an active admin session:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pretix.control.permissions import (
|
||||
StaffMemberRequiredMixin, staff_member_required
|
||||
|
||||
@@ -39,7 +39,9 @@ subclass that also adds support for internationalized fields:
|
||||
|
||||
.. autoclass:: pretix.base.forms.SettingsForm
|
||||
|
||||
You can simply use it like this::
|
||||
You can simply use it like this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class EventSettingsForm(SettingsForm):
|
||||
show_date_to = forms.BooleanField(
|
||||
@@ -56,7 +58,9 @@ You can simply use it like this::
|
||||
Defaults in plugins
|
||||
-------------------
|
||||
|
||||
Plugins can add custom hardcoded defaults in the following way::
|
||||
Plugins can add custom hardcoded defaults in the following way:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pretix.base.settings import settings_hierarkey
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "3.14.1"
|
||||
__version__ = "3.15.0.dev0"
|
||||
|
||||
@@ -292,6 +292,8 @@ 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.'),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
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,
|
||||
@@ -303,4 +304,5 @@ 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'),
|
||||
]
|
||||
|
||||
@@ -71,7 +71,7 @@ class GeoCodeView(LoginRequiredMixin, View):
|
||||
|
||||
try:
|
||||
r = requests.get(
|
||||
'http://www.mapquestapi.com/geocoding/v1/address?location={}&key={}'.format(
|
||||
'https://www.mapquestapi.com/geocoding/v1/address?location={}&key={}'.format(
|
||||
quote(q), gs.settings.mapquest_apikey
|
||||
)
|
||||
)
|
||||
|
||||
@@ -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-22 06:00+0000\n"
|
||||
"PO-Revision-Date: 2020-12-23 04: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,6 +1484,8 @@ 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."
|
||||
@@ -8155,18 +8157,16 @@ msgid "Free text input"
|
||||
msgstr "Vrije tekstinvoer"
|
||||
|
||||
#: pretix/control/forms/event.py:575
|
||||
#, fuzzy
|
||||
#| msgid "Do not copy"
|
||||
msgid "Do not ask"
|
||||
msgstr "Niet kopiëren"
|
||||
msgstr "Niet vragen"
|
||||
|
||||
#: pretix/control/forms/event.py:576
|
||||
msgid "Ask, but do not require input"
|
||||
msgstr ""
|
||||
msgstr "Vraag, maar maak invoer niet verplicht"
|
||||
|
||||
#: pretix/control/forms/event.py:577
|
||||
msgid "Ask and require input"
|
||||
msgstr ""
|
||||
msgstr "Vraag, invullen is niet verplicht"
|
||||
|
||||
#: pretix/control/forms/event.py:612
|
||||
msgid ""
|
||||
@@ -8616,16 +8616,12 @@ msgid "Cancellations"
|
||||
msgstr "Annuleringen"
|
||||
|
||||
#: pretix/control/forms/filter.py:153
|
||||
#, fuzzy
|
||||
#| msgid "Canceled (paid fee)"
|
||||
msgid "Canceled (fully)"
|
||||
msgstr "Geannuleerd (betaalde kosten)"
|
||||
msgstr "Geannuleerd (hele bestelling)"
|
||||
|
||||
#: pretix/control/forms/filter.py:154
|
||||
#, fuzzy
|
||||
#| msgid "Canceled (or with paid fee)"
|
||||
msgid "Canceled (fully or with paid fee)"
|
||||
msgstr "Geannuleerd (of met betaalde kosten)"
|
||||
msgstr "Geannuleerd (hele bestelling of met betaalde kosten)"
|
||||
|
||||
#: pretix/control/forms/filter.py:155
|
||||
msgid "Cancellation requested"
|
||||
@@ -8661,17 +8657,12 @@ msgid "Overpaid"
|
||||
msgstr "Overbetaald"
|
||||
|
||||
#: pretix/control/forms/filter.py:162
|
||||
#, fuzzy
|
||||
#| msgid "Mark as paid"
|
||||
msgid "Partially paid"
|
||||
msgstr "Markeren als betaald"
|
||||
msgstr "Gedeeltelijk betaald"
|
||||
|
||||
#: pretix/control/forms/filter.py:163
|
||||
#, fuzzy
|
||||
#| msgctxt "checkoutflow"
|
||||
#| msgid "Order confirmed"
|
||||
msgid "Underpaid (but confirmed)"
|
||||
msgstr "Bestelling bevestigd"
|
||||
msgstr "Onderbetaald (maar bevestigd)"
|
||||
|
||||
#: pretix/control/forms/filter.py:164
|
||||
msgid "Pending (but fully paid)"
|
||||
@@ -15141,10 +15132,8 @@ msgid "Remove filter"
|
||||
msgstr "Verwijder filter"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/orders/index.html:116
|
||||
#, fuzzy
|
||||
#| msgid "Order total"
|
||||
msgid "Order paid / total"
|
||||
msgstr "Totaalbedrag van bestelling"
|
||||
msgstr "Bestelling betaald / totaal"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/orders/index.html:151
|
||||
#: pretix/control/templates/pretixcontrol/search/orders.html:83
|
||||
@@ -18798,7 +18787,7 @@ msgstr "De bestelling is al geannuleerd."
|
||||
|
||||
#: pretix/plugins/banktransfer/tasks.py:117
|
||||
msgid "Automatic split to multiple orders not possible."
|
||||
msgstr ""
|
||||
msgstr "Automatisch uitsplitsen naar meerdere bestellingen niet mogelijk."
|
||||
|
||||
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/checkout_payment_form.html:4
|
||||
msgid ""
|
||||
@@ -18827,11 +18816,8 @@ msgid "Account"
|
||||
msgstr "Rekening"
|
||||
|
||||
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/control.html:22
|
||||
#, fuzzy
|
||||
#| msgctxt "amount_label"
|
||||
#| msgid "Transfer"
|
||||
msgid "Transfer amount"
|
||||
msgstr "Overdragen"
|
||||
msgstr "Overschrijvingsbedrag"
|
||||
|
||||
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/control.html:26
|
||||
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/import_assign.html:33
|
||||
|
||||
@@ -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,
|
||||
name=a.item.tax_rule.name if a.item.tax_rule else "",
|
||||
rate=a.tax_rate,
|
||||
)
|
||||
else:
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
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 _
|
||||
@@ -18,12 +20,52 @@ 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()],
|
||||
widget=forms.EmailInput(attrs={'autocomplete': 'section-contact email'})
|
||||
)
|
||||
email = forms.EmailField(
|
||||
label=_('E-mail'),
|
||||
validators=[
|
||||
EmailBanlistValidator(),
|
||||
EmailDNSValidator(),
|
||||
],
|
||||
widget=forms.EmailInput(attrs={'autocomplete': 'section-contact email'})
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.event = kwargs.pop('event')
|
||||
|
||||
@@ -183,7 +183,7 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if not cart_namespace %}
|
||||
{% if not cart_namespace or subevent %}
|
||||
<div>
|
||||
{% if ev.location %}
|
||||
<div class="info-row">
|
||||
|
||||
@@ -120,8 +120,10 @@ 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():
|
||||
self.order.email_known_to_work = True
|
||||
self.order.save(update_fields=['email_known_to_work'])
|
||||
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'])
|
||||
return redirect(self.get_order_url())
|
||||
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ oauthlib==3.1.*
|
||||
django-jsonfallback>=2.1.2
|
||||
psycopg2-binary
|
||||
tqdm==4.*
|
||||
dnspython==1.16.*
|
||||
# Stripe
|
||||
stripe==2.42.*
|
||||
# PayPal
|
||||
|
||||
@@ -134,6 +134,7 @@ setup(
|
||||
'django-i18nfield>=1.7.0',
|
||||
'django-jsonfallback>=2.1.2',
|
||||
'psycopg2-binary',
|
||||
'dnspython==1.16.*',
|
||||
'tqdm==4.*',
|
||||
'vobject==0.9.*',
|
||||
'pycountry',
|
||||
|
||||
@@ -55,6 +55,10 @@ 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)
|
||||
|
||||
Reference in New Issue
Block a user