Added custom error pages

This commit is contained in:
Raphael Michel
2015-09-17 23:44:07 +02:00
parent 59e4b19e3f
commit c8830cc880
20 changed files with 129 additions and 37 deletions

View File

@@ -0,0 +1,13 @@
{% extends "error.html" %}
{% load i18n %}
{% block title %}{% trans "Bad Request" %}{% endblock %}
{% block content %}
<i class="fa fa-frown-o big-icon"></i>
<h1>{% trans "Bad Request" %}</h1>
<p>{% trans "We were unable to parse your request." %}</p>
<p>{{ exception }}</p>
<p>
<a href="javascript:history.back()">{% trans "Take a step back" %}</a>
&middot; <a href="javascript:location.reload()">{% trans "Try again" %}</a>
</p>
{% endblock %}

View File

@@ -0,0 +1,13 @@
{% extends "error.html" %}
{% load i18n %}
{% block title %}{% trans "Permission denied" %}{% endblock %}
{% block content %}
<i class="fa fa-lock big-icon"></i>
<h1>{% trans "Permission denied" %}</h1>
<p>{% trans "You do not have access to this page." %}</p>
<p>{{ exception }}</p>
<p>
<a href="javascript:history.back()">{% trans "Take a step back" %}</a>
&middot; <a href="javascript:location.reload()">{% trans "Try again" %}</a>
</p>
{% endblock %}

View File

@@ -0,0 +1,12 @@
{% extends "error.html" %}
{% load i18n %}
{% block title %}{% trans "Not found" %}{% endblock %}
{% block content %}
<i class="fa fa-meh-o big-icon"></i>
<h1>{% trans "Not found" %}</h1>
<p>{% trans "I'm afraid we could not find the the resource you requested." %}</p>
<p>{{ exception }}</p>
<p>
<a href="javascript:history.back()">{% trans "Take a step back" %}</a>
</p>
{% endblock %}

View File

@@ -0,0 +1,14 @@
{% extends "error.html" %}
{% load i18n %}
{% block title %}{% trans "Internal Server Error" %}{% endblock %}
{% block content %}
<i class="fa fa-bolt big-icon"></i>
<h1>{% trans "Internal Server Error" %}</h1>
<p>{% trans "We had trouble processing your request." %}</p>
<p>{% trans "If this problem persists, please contact us." %}</p>
<p>{{ exception }}</p>
<p>
<a href="javascript:history.back()">{% trans "Take a step back" %}</a>
&middot; <a href="javascript:location.reload()">{% trans "Try again" %}</a>
</p>
{% endblock %}

View File

@@ -0,0 +1,18 @@
{% load compress %}
{% load i18n %}
{% load staticfiles %}
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}{% endblock %}</title>
{% compress css %}
<link rel="stylesheet" type="text/less" href="{% static "pretixbase/less/error.less" %}" />
{% endcompress %}
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div class="container">
{% block content %}{% endblock %}
</div>
</body>
</html>

View File

@@ -6,7 +6,7 @@
<head>
<title>{{ settings.PRETIX_INSTANCE_NAME }}</title>
{% compress css %}
<link rel="stylesheet" type="text/less" href="{% static "pretixpresale/less/cachedfiles.less" %}" />
<link rel="stylesheet" type="text/less" href="{% static "pretixbase/less/cachedfiles.less" %}" />
{% endcompress %}
{% compress js %}
<script type="text/javascript" src="{% static "jquery/js/jquery-2.1.1.min.js" %}"></script>

View File

View File

@@ -7,7 +7,7 @@ from pretix.base.models import CachedFile
class DownloadView(TemplateView):
template_name = "pretixpresale/cachedfiles/pending.html"
template_name = "pretixbase/cachedfiles/pending.html"
@cached_property
def object(self):

View File

@@ -3,7 +3,7 @@ from urllib.parse import urlparse
from django.conf import settings
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.core.urlresolvers import get_script_prefix, resolve
from django.http import HttpResponseNotFound
from django.http import Http404
from django.shortcuts import resolve_url
from django.utils.encoding import force_str
from django.utils.translation import ugettext as _
@@ -12,7 +12,6 @@ from pretix.base.models import Event, EventPermission, Organizer
class PermissionMiddleware:
"""
This middleware enforces all requests to the control app to require login.
Additionally, it enforces all requests to "control:event." URLs
@@ -43,6 +42,7 @@ class PermissionMiddleware:
(not login_netloc or login_netloc == current_netloc)):
path = request.get_full_path()
from django.contrib.auth.views import redirect_to_login
return redirect_to_login(
path, resolved_login_url, REDIRECT_FIELD_NAME)
@@ -61,8 +61,8 @@ class PermissionMiddleware:
)
request.organizer = request.event.organizer
except IndexError:
return HttpResponseNotFound(_("The selected event was not found or you "
"have no permission to administrate it."))
raise Http404(_("The selected event was not found or you "
"have no permission to administrate it."))
elif 'organizer' in url.kwargs:
try:
request.organizer = Organizer.objects.current.filter(
@@ -70,5 +70,5 @@ class PermissionMiddleware:
permitted__id__exact=request.user.id,
)[0]
except IndexError:
return HttpResponseNotFound(_("The selected organizer was not found or you "
"have no permission to administrate it."))
return Http404(_("The selected organizer was not found or you "
"have no permission to administrate it."))

View File

@@ -1,4 +1,4 @@
from django.http import HttpResponseForbidden
from django.core.exceptions import PermissionDenied
from django.utils.translation import ugettext as _
from pretix.base.models import EventPermission, OrganizerPermission
@@ -13,7 +13,7 @@ def event_permission_required(permission):
def wrapper(request, *args, **kw):
if not request.user.is_authenticated(): # NOQA
# just a double check, should not ever happen
return HttpResponseForbidden()
raise PermissionDenied()
try:
perm = EventPermission.objects.current.get(
event=request.event,
@@ -30,7 +30,7 @@ def event_permission_required(permission):
pass
if allowed:
return function(request, *args, **kw)
return HttpResponseForbidden(_('You do not have permission to view this content.'))
raise PermissionDenied(_('You do not have permission to view this content.'))
return wrapper
return decorator
@@ -57,7 +57,7 @@ def organizer_permission_required(permission):
def wrapper(request, *args, **kw):
if not request.user.is_authenticated(): # NOQA
# just a double check, should not ever happen
return HttpResponseForbidden()
raise PermissionDenied()
try:
perm = OrganizerPermission.objects.current.get(
organizer=request.organizer,
@@ -74,7 +74,7 @@ def organizer_permission_required(permission):
pass
if allowed or request.user.is_superuser:
return function(request, *args, **kw)
return HttpResponseForbidden(_('You do not have permission to view this content.'))
raise PermissionDenied(_('You do not have permission to view this content.'))
return wrapper
return decorator

View File

@@ -4,7 +4,7 @@ from django.contrib import messages
from django.core.urlresolvers import resolve, reverse
from django.db import transaction
from django.forms.models import inlineformset_factory
from django.http import Http404, HttpResponseForbidden, HttpResponseRedirect
from django.http import Http404, HttpResponseRedirect
from django.shortcuts import redirect
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _
@@ -380,7 +380,8 @@ class PropertyDelete(EventPermissionRequiredMixin, DeleteView):
messages.success(request, _('The selected property has been deleted.'))
return HttpResponseRedirect(success_url)
else:
return HttpResponseForbidden()
messages.error(request, _('The selected property can not be deleted.'))
return HttpResponseRedirect(self.get_success_url())
def get_success_url(self) -> str:
return reverse('control:event.items.properties', kwargs={

View File

@@ -1,6 +1,6 @@
from django.contrib import messages
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse
from django.http import HttpResponseForbidden
from django.utils.translation import ugettext_lazy as _
from django.views.generic import CreateView, ListView, UpdateView
@@ -52,7 +52,7 @@ class OrganizerCreate(CreateView):
def dispatch(self, request, *args, **kwargs):
if not request.user.is_superuser:
return HttpResponseForbidden() # TODO
raise PermissionDenied() # TODO
return super().dispatch(request, *args, **kwargs)
def form_valid(self, form):

View File

@@ -1,5 +1,6 @@
from django.core.urlresolvers import resolve
from django.http import HttpResponseNotFound
from django.http import Http404
from django.utils.translation import ugettext_lazy as _
from pretix.base.models import Event
@@ -30,4 +31,4 @@ class EventMiddleware:
organizer__slug=url.kwargs['organizer'],
).select_related('organizer')[0]
except IndexError:
return HttpResponseNotFound('Unknown event') # TODO: Provide error message
return Http404(_('The selected event was not found.'))

View File

@@ -1,6 +1,5 @@
from django.conf.urls import include, url
import pretix.presale.views.cachedfiles
import pretix.presale.views.cart
import pretix.presale.views.checkout
import pretix.presale.views.event
@@ -8,8 +7,6 @@ import pretix.presale.views.locale
import pretix.presale.views.order
urlpatterns = [
url(r'^download/(?P<id>[^/]+)/$', pretix.presale.views.cachedfiles.DownloadView.as_view(),
name='cachedfile.download'),
url(r'^(?P<organizer>[^/]+)/(?P<event>[^/]+)/', include([
url(r'^$', pretix.presale.views.event.EventIndex.as_view(), name='event.index'),
url(r'^cart/add$', pretix.presale.views.cart.CartAdd.as_view(), name='event.cart.add'),

View File

@@ -3,15 +3,18 @@ from datetime import timedelta
from django.contrib import messages
from django.core.urlresolvers import reverse
from django.db.models import Q
from django.http import HttpResponseForbidden, HttpResponseNotFound
from django.http import Http404
from django.shortcuts import redirect
from django.utils.functional import cached_property
from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _
from django.views.generic import TemplateView, View
from pretix.base.models import CachedFile, CachedTicket, Order, OrderPosition
from pretix.base.services.tickets import generate
from pretix.base.signals import register_payment_providers, register_ticket_outputs
from pretix.base.signals import (
register_payment_providers, register_ticket_outputs,
)
from pretix.presale.views import CartDisplayMixin, EventViewMixin
from pretix.presale.views.checkout import QuestionsViewMixin
@@ -49,7 +52,7 @@ class OrderDetails(EventViewMixin, OrderDetailMixin, CartDisplayMixin, TemplateV
def get(self, request, *args, **kwargs):
self.kwargs = kwargs
if not self.order:
return HttpResponseNotFound(_('Unknown order code or order does belong to another user.'))
raise Http404(_('Unknown order code or order does belong to another user.'))
return super().get(request, *args, **kwargs)
@cached_property
@@ -101,7 +104,7 @@ class OrderPay(EventViewMixin, OrderDetailMixin, TemplateView):
def dispatch(self, request, *args, **kwargs):
self.request = request
if not self.order:
return HttpResponseNotFound(_('Unknown order code or order does belong to another user.'))
raise Http404(_('Unknown order code or order does belong to another user.'))
if (self.order.status not in (Order.STATUS_PENDING, Order.STATUS_EXPIRED)
or not self.payment_provider.order_can_retry(self.order)
or not self.payment_provider.is_enabled):
@@ -144,7 +147,7 @@ class OrderPayDo(EventViewMixin, OrderDetailMixin, TemplateView):
def dispatch(self, request, *args, **kwargs):
self.request = request
if not self.order:
return HttpResponseNotFound(_('Unknown order code or order does belong to another user.'))
raise Http404(_('Unknown order code or order does belong to another user.'))
if not self.payment_provider.order_can_retry(self.order) or not self.payment_provider.is_enabled:
messages.error(request, _('The payment for this order cannot be continued.'))
return redirect(self.get_order_url())
@@ -207,9 +210,11 @@ class OrderModify(EventViewMixin, OrderDetailMixin, QuestionsViewMixin, Template
self.request = request
self.kwargs = kwargs
if not self.order:
return HttpResponseNotFound(_('Unknown order code or order does belong to another user.'))
messages.error(request, _('Unknown order code or order does belong to another user.'))
return redirect(self.get_order_url())
if not self.order.can_modify_answers:
return HttpResponseForbidden(_('You cannot modify this order'))
messages.error(request, _('You cannot modify this order'))
return redirect(self.get_order_url())
return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
@@ -226,9 +231,10 @@ class OrderCancel(EventViewMixin, OrderDetailMixin, TemplateView):
self.request = request
self.kwargs = kwargs
if not self.order:
return HttpResponseNotFound(_('Unknown order code or order does belong to another user.'))
raise Http404(_('Unknown order code or order does belong to another user.'))
if self.order.status not in (Order.STATUS_PENDING, Order.STATUS_EXPIRED):
return HttpResponseForbidden(_('You cannot cancel this order'))
messages.error(request, _('You cannot cancel this order'))
return redirect(self.get_order_url())
return super().dispatch(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
@@ -260,7 +266,7 @@ class OrderDownload(EventViewMixin, OrderDetailMixin, View):
messages.error(request, _('You requested an invalid ticket output type.'))
return redirect(self.get_order_url())
if not self.order:
return HttpResponseNotFound(_('Unknown order code or order does belong to another user.'))
raise Http404(_('Unknown order code or order does belong to another user.'))
if self.order.status != Order.STATUS_PAID:
messages.error(request, _('Order is not paid.'))
return redirect(self.get_order_url())

View File

@@ -5,10 +5,13 @@ from django.apps import apps
from django.conf import settings
from django.conf.urls import include, url
import pretix.base.views.cachedfiles
import pretix.control.urls
import pretix.presale.urls
urlpatterns = [
url(r'^download/(?P<id>[^/]+)/$', pretix.base.views.cachedfiles.DownloadView.as_view(),
name='cachedfile.download'),
url(r'^control/', include(pretix.control.urls, namespace='control')),
# The pretixpresale namespace is configured at the bottom of this file, because it
# contains a wildcard-style URL which has to be configured _after_ debug settings.

View File

@@ -1,11 +1,9 @@
@import "../../bootstrap/less/bootstrap.less";
@import "../../fontawesome/less/font-awesome.less";
@import "../../lightbox/css/lightbox.css";
@import "colors.less";
@fa-font-path: "../../fontawesome/fonts";
@brand-primary: #8E44B3;
body {
background: #ececec;
text-align: center;

View File

@@ -0,0 +1 @@
@brand-primary: #8E44B3;

View File

@@ -0,0 +1,16 @@
@import "../../bootstrap/less/bootstrap.less";
@import "../../fontawesome/less/font-awesome.less";
@import "colors.less";
@fa-font-path: "../../fontawesome/fonts";
body {
background: #ececec;
text-align: center;
padding: 50px 0;
}
.big-icon {
font-size: 200px;
color: @brand-primary;
}

View File

@@ -2,8 +2,7 @@
@import "../../fontawesome/less/font-awesome.less";
@import "../../lightbox/css/lightbox.css";
@fa-font-path: "../../fontawesome/fonts";
@brand-primary: #8E44B3;
@import "../../pretixbase/less/colors.less";
@import "event.less";
@import "forms.less";