mirror of
https://github.com/pretix/pretix.git
synced 2026-05-04 15:04:03 +00:00
Add new bundled plugin "returnurl"
This commit is contained in:
@@ -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 *
|
||||
|
||||
21
src/pretix/plugins/returnurl/__init__.py
Normal file
21
src/pretix/plugins/returnurl/__init__.py
Normal 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'
|
||||
50
src/pretix/plugins/returnurl/signals.py
Normal file
50
src/pretix/plugins/returnurl/signals.py
Normal 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',
|
||||
}]
|
||||
@@ -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 %}
|
||||
8
src/pretix/plugins/returnurl/urls.py
Normal file
8
src/pretix/plugins/returnurl/urls.py
Normal 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'),
|
||||
]
|
||||
30
src/pretix/plugins/returnurl/views.py
Normal file
30
src/pretix/plugins/returnurl/views.py
Normal 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
|
||||
})
|
||||
@@ -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()
|
||||
|
||||
@@ -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',
|
||||
|
||||
74
src/tests/plugins/test_returnurl.py
Normal file
74
src/tests/plugins/test_returnurl.py
Normal 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
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user