Add new bundled plugin "returnurl"

This commit is contained in:
Raphael Michel
2019-08-13 11:56:39 +02:00
parent 985f354293
commit d972cd4c49
10 changed files with 207 additions and 1 deletions

View File

@@ -22,3 +22,5 @@ recursive-include pretix/plugins/ticketoutputpdf/templates *
recursive-include pretix/plugins/ticketoutputpdf/static *
recursive-include pretix/plugins/badges/templates *
recursive-include pretix/plugins/badges/static *
recursive-include pretix/plugins/returnurl/templates *
recursive-include pretix/plugins/returnurl/static *

View File

@@ -0,0 +1,21 @@
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
from pretix import __version__ as version
class ReturnURLApp(AppConfig):
name = 'pretix.plugins.returnurl'
verbose_name = _("Redirection from order page")
class PretixPluginMeta:
name = _("Redirection from order page")
author = _("the pretix team")
version = version
description = _("This plugin allows to link to payments and redirect back afterwards.")
def ready(self):
from . import signals # NOQA
default_app_config = 'pretix.plugins.returnurl.ReturnURLApp'

View File

@@ -0,0 +1,50 @@
from django.core.exceptions import PermissionDenied
from django.dispatch import receiver
from django.shortcuts import redirect
from django.urls import resolve, reverse
from django.utils.translation import ugettext_lazy as _
from pretix.control.signals import nav_event_settings
from pretix.presale.signals import process_request
@receiver(process_request, dispatch_uid="returnurl_process_request")
def returnurl_process_request(sender, request, **kwargs):
try:
r = resolve(request.path_info)
except:
return
urlname = r.url_name
urlkwargs = r.kwargs
if urlname.startswith('event.order'):
key = 'order_{}_{}_{}_return_url'.format(urlkwargs.get('organizer', '-'), urlkwargs['event'],
urlkwargs['order'])
if urlname == 'event.order' and key in request.session:
r = redirect(request.session.get(key))
del request.session[key]
return r
elif urlname != 'event.order' and 'return_url' in request.GET:
u = request.GET.get('return_url')
if not sender.settings.returnurl_prefix:
raise PermissionDenied('No return URL prefix set.')
elif not u.startswith(sender.settings.returnurl_prefix):
raise PermissionDenied('Invalid return URL.')
request.session[key] = u
@receiver(nav_event_settings, dispatch_uid='returnurl_nav')
def navbar_info(sender, request, **kwargs):
url = resolve(request.path_info)
if not request.user.has_event_permission(request.organizer, request.event, 'can_change_event_settings',
request=request):
return []
return [{
'label': _('Redirection'),
'url': reverse('plugins:returnurl:settings', kwargs={
'event': request.event.slug,
'organizer': request.organizer.slug,
}),
'active': url.namespace == 'plugins:returnurl',
}]

View File

@@ -0,0 +1,16 @@
{% extends "pretixcontrol/event/settings_base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% block inside %}
<h1>{% trans "Redirection from order page" %}</h1>
<form action="" method="post" class="form-horizontal" enctype="multipart/form-data">
{% csrf_token %}
{% bootstrap_form_errors form %}
{% bootstrap_field form.returnurl_prefix layout="horizontal" %}
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}
</button>
</div>
</form>
{% endblock %}

View File

@@ -0,0 +1,8 @@
from django.conf.urls import url
from .views import ReturnSettings
urlpatterns = [
url(r'^control/event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/returnurl/settings$',
ReturnSettings.as_view(), name='settings'),
]

View File

@@ -0,0 +1,30 @@
from django import forms
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
from pretix.base.forms import SettingsForm
from pretix.base.models import Event
from pretix.control.views.event import (
EventSettingsFormView, EventSettingsViewMixin,
)
class ReturnSettingsForm(SettingsForm):
returnurl_prefix = forms.URLField(
label=_("Base redirection URL"),
help_text=_("Redirection will only be allowed to URLs that start with this prefix."),
required=False,
)
class ReturnSettings(EventSettingsViewMixin, EventSettingsFormView):
model = Event
form_class = ReturnSettingsForm
template_name = 'returnurl/settings.html'
permission = 'can_change_settings'
def get_success_url(self) -> str:
return reverse('plugins:returnurl:settings', kwargs={
'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug
})

View File

@@ -46,6 +46,7 @@ from pretix.presale.views.robots import NoSearchIndexViewMixin
class OrderDetailMixin(NoSearchIndexViewMixin):
@cached_property
def order(self):
order = self.request.event.orders.filter(code=self.kwargs['order']).select_related('event').first()

View File

@@ -279,6 +279,7 @@ INSTALLED_APPS = [
'pretix.plugins.pretixdroid',
'pretix.plugins.badges',
'pretix.plugins.manualpayment',
'pretix.plugins.returnurl',
'django_markup',
'django_otp',
'django_otp.plugins.otp_totp',

View File

@@ -0,0 +1,74 @@
from decimal import Decimal
from django_scopes import scopes_disabled
from pretix.base.models import OrderPayment
from ..presale.test_orders import BaseOrdersTest
class ReturnURLTest(BaseOrdersTest):
@scopes_disabled()
def setUp(self):
super().setUp()
self.event.enable_plugin("pretix.plugins.returnurl")
self.event.save()
self.event.settings.returnurl_prefix = 'https://example.com'
self.event.settings.set('payment_banktransfer__enabled', True)
self.event.settings.set('payment_testdummy__enabled', True)
self.order.payments.create(
provider='manual',
state=OrderPayment.PAYMENT_STATE_CONFIRMED,
amount=Decimal('10.00'),
)
def test_redirect_once(self):
r = self.client.get(
'/%s/%s/order/%s/%s/pay/change?return_url=https://example.com/foo/var/' % (
self.orga.slug, self.event.slug, self.order.code, self.order.secret
)
)
assert r.status_code == 200
r = self.client.post(
'/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
{
'payment': 'banktransfer'
},
follow=False
)
assert r['Location'].endswith('/confirm')
r = self.client.post(
r['Location'],
follow=False
)
assert r['Location'] == '/%s/%s/order/%s/%s/' % (
self.orga.slug, self.event.slug, self.order.code, self.order.secret
)
r = self.client.get(
r['Location'],
follow=False
)
assert r['Location'] == 'https://example.com/foo/var/'
r = self.client.get(
'/%s/%s/order/%s/%s/' % (
self.orga.slug, self.event.slug, self.order.code, self.order.secret
)
)
assert r.status_code == 200
def test_redirect_enforce_prefix_match(self):
r = self.client.get(
'/%s/%s/order/%s/%s/pay/change?return_url=https://example.org/foo/var/' % (
self.orga.slug, self.event.slug, self.order.code, self.order.secret
)
)
assert r.status_code == 403
def test_redirect_enforce_prefix_set(self):
del self.event.settings.returnurl_prefix
r = self.client.get(
'/%s/%s/order/%s/%s/pay/change?return_url=https://example.org/foo/var/' % (
self.orga.slug, self.event.slug, self.order.code, self.order.secret
)
)
assert r.status_code == 403

View File

@@ -17,7 +17,8 @@ from pretix.base.reldate import RelativeDate, RelativeDateWrapper
from pretix.base.services.invoices import generate_invoice
class OrdersTest(TestCase):
class BaseOrdersTest(TestCase):
@scopes_disabled()
def setUp(self):
super().setUp()
@@ -82,6 +83,8 @@ class OrdersTest(TestCase):
total=Decimal("23")
)
class OrdersTest(BaseOrdersTest):
def test_unknown_order(self):
response = self.client.get(
'/%s/%s/order/ABCDE/123/' % (self.orga.slug, self.event.slug)