Move timemachine controls into presale, implement session transfer for multidomain time machine support

This commit is contained in:
Mira Weller
2024-04-30 21:49:58 +02:00
parent 5b1175ff05
commit 64358be4ae
12 changed files with 139 additions and 65 deletions
+1 -1
View File
@@ -49,7 +49,7 @@ class TimeMachineMiddleware:
def __call__(self, request):
if hasattr(request, 'event') and hasattr(request, '_namespace') and request._namespace == 'presale' and \
'timemachine_now_dt' in request.session and \
request.event.testmode and \
request.event.testmode and request.user.is_authenticated and \
request.user.has_event_permission(request.organizer, request.event, 'can_change_event_settings', request):
request.now_dt = parse(request.session['timemachine_now_dt'])
request.now_dt_is_fake = True
+1 -1
View File
@@ -106,7 +106,7 @@ def _default_context(request):
else:
ctx['complain_testmode_orders'] = False
if not request.event.live and ctx['has_domain']:
if (request.event.testmode or not request.event.live) and ctx['has_domain']:
child_sess = request.session.get('child_session_{}'.format(request.event.pk))
s = SessionStore()
if not child_sess or not s.exists(child_sess):
@@ -124,35 +124,6 @@
<div class="clear"></div>
</div>
</div>
{% if request.event.testmode %}
<div class="panel {% if request.session.timemachine_now_dt %}panel-success{% else %}panel-default{% endif %}">
<div class="panel-heading">
{% trans "Time machine" %}
</div>
<div class="panel-body">
<form action="" method="post" class="">
{% csrf_token %}
{% bootstrap_form_errors timemachine_form %}
<p>{% trans "Test your shop as if it were a different date and time." %}</p>
{% bootstrap_field timemachine_form.now_dt layout="control" show_label=False field_class="col-md-6" %}
<div class="text-right">
<button type="submit" class="btn btn-default btn-lg btn-save">
{% if request.session.timemachine_now_dt %}{% trans "Change" %}{% else %}{% trans "Enable time machine" %}{% endif %}
</button>
{% if request.session.timemachine_now_dt %}
<button name="timemachine_disable" value="true" type="submit" class="btn btn-default btn-lg btn-save">
{% trans "Disable" %}
</button>
{% endif %}
</div>
</form>
<div class="clear"></div>
</div>
</div>
{% endif %}
<div class="text-right">
<a href="{% url "control:event.dangerzone" organizer=request.organizer.slug event=request.event.slug %}"
class="btn btn-default btn-lg">
@@ -0,0 +1,9 @@
{% load eventurl %}
{% load static %}
<form action="{% eventurl request.event "presale:event.auth" %}" method="post">
<input type="hidden" value="{{ new_session }}" name="session">
<button type="submit">
Continue
</button>
</form>
<script src="{% static "pretixcontrol/js/send_form.js" %}"></script>
+1
View File
@@ -247,6 +247,7 @@ urlpatterns = [
re_path(r'^widgets.json$', dashboards.event_index_widgets_lazy, name='event.index.widgets'),
re_path(r'^logs/embed$', dashboards.event_index_log_lazy, name='event.index.logs'),
re_path(r'^live/$', event.EventLive.as_view(), name='event.live'),
re_path(r'^transfer_session/$', event.EventTransferSession.as_view(), name='event.transfer_session'),
re_path(r'^logs/$', event.EventLog.as_view(), name='event.log'),
re_path(r'^delete/$', event.EventDelete.as_view(), name='event.delete'),
re_path(r'^comment/$', event.EventComment.as_view(),
+8 -28
View File
@@ -46,8 +46,6 @@ from zoneinfo import ZoneInfo
import bleach
import qrcode
import qrcode.image.svg
from dateutil import parser
from django import forms
from django.apps import apps
from django.conf import settings
from django.contrib import messages
@@ -65,7 +63,7 @@ from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse
from django.utils.functional import cached_property
from django.utils.http import url_has_allowed_host_and_scheme
from django.utils.timezone import get_current_timezone, now
from django.utils.timezone import now
from django.utils.translation import gettext, gettext_lazy as _, gettext_noop
from django.views.generic import FormView, ListView
from django.views.generic.base import TemplateView, View
@@ -94,12 +92,11 @@ from pretix.control.views.mailsetup import MailSettingsSetupView
from pretix.control.views.user import RecentAuthenticationRequiredMixin
from pretix.helpers.database import rolledback_transaction
from pretix.multidomain.urlreverse import (
build_absolute_uri, eventreverse, get_event_domain,
build_absolute_uri, get_event_domain,
)
from pretix.plugins.stripe.payment import StripeSettingsHolder
from pretix.presale.style import regenerate_css
from ...base.forms.widgets import SplitDateTimePickerWidget
from ...base.i18n import language
from ...base.models.items import (
Item, ItemCategory, ItemMetaProperty, Question, Quota,
@@ -955,43 +952,21 @@ class EventPermissions(EventSettingsViewMixin, EventPermissionRequiredMixin, Tem
template_name = 'pretixcontrol/event/permissions.html'
class TimemachineForm(forms.Form):
now_dt = forms.SplitDateTimeField(
label=_('Fake date time'),
widget=SplitDateTimePickerWidget(),
initial=lambda: now().astimezone(get_current_timezone())
)
class EventLive(EventPermissionRequiredMixin, TemplateView):
permission = 'can_change_event_settings'
template_name = 'pretixcontrol/event/live.html'
def setup(self, request, *args, **kwargs):
super().setup(request, *args, **kwargs)
self.timemachine_form = TimemachineForm(
data=request.method == 'POST' and request.POST or None,
initial=(
{'now_dt': parser.parse(request.session.get('timemachine_now_dt', None))}
if request.session.get('timemachine_now_dt', None) else {}
)
)
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['issues'] = self.request.event.live_issues
ctx['actual_orders'] = self.request.event.orders.filter(testmode=False).exists()
ctx['timemachine_form'] = self.timemachine_form
return ctx
def post(self, request, *args, **kwargs):
if request.POST.get("timemachine_disable"):
del request.session['timemachine_now_dt']
messages.success(self.request, _('Time machine disabled!'))
elif self.timemachine_form.is_valid():
request.session['timemachine_now_dt'] = str(self.timemachine_form.cleaned_data['now_dt'])
return redirect(eventreverse(request.event, "presale:event.index"))
elif request.POST.get("live") == "true" and not self.request.event.live_issues:
if request.POST.get("live") == "true" and not self.request.event.live_issues:
with transaction.atomic():
request.event.live = True
request.event.save()
@@ -1047,6 +1022,11 @@ class EventLive(EventPermissionRequiredMixin, TemplateView):
})
class EventTransferSession(EventPermissionRequiredMixin, TemplateView):
permission = 'can_change_event_settings'
template_name = 'pretixcontrol/event/transfer_session.html'
class EventDelete(RecentAuthenticationRequiredMixin, EventPermissionRequiredMixin, FormView):
permission = 'can_change_event_settings'
template_name = 'pretixcontrol/event/delete.html'
@@ -38,10 +38,10 @@
<div class="container">
<span class="fa fa-clock-o" aria-hidden="true"></span>
{% trans "You are currently using the time machine. The ticket shop is rendered as if it were" %} {{ request.now_dt }}
<a href="{{ site_url }}{% url "control:event.live" event=event.slug organizer=event.organizer.slug %}" class="btn-link">
<a href="{% eventurl event "presale:event.timemachine" %}" class="btn-link">
{% trans "Change" %}
</a>
<form action="{{ site_url }}{% url "control:event.live" event=event.slug organizer=event.organizer.slug %}" method="post" class="download-btn-form">
<form action="{% eventurl event "presale:event.timemachine" %}" method="post" class="download-btn-form">
{% csrf_token %}
<button type="submit" class="btn-link" value="true" name="timemachine_disable">{% trans "Go back to current time" %}</button>
</form>
@@ -126,6 +126,15 @@
{% if request.event.testmode %}
{% if request.sales_channel.testmode_supported %}
<div class="alert alert-warning">
{% if request.user.is_authenticated %}
<a href="{% eventurl event "presale:event.timemachine" %}" class="btn btn-default pull-right" title="{% trans "Enable time machine" %}">
<span class="fa fa-clock-o" aria-hidden="true"></span>
</a>
{% elif request.event_domain or request.organizer_domain %}
<a href="{% url "control:event.transfer_session" event=event.slug organizer=event.organizer.slug %}" class="btn btn-default pull-right" title="{% trans "Enable time machine" %}">
<span class="fa fa-clock-o" aria-hidden="true"></span>
</a>
{% endif %}
<p><strong><span class="sr-only">{% trans "Warning" context "alert-messages" %}:</span>
{% trans "This ticket shop is currently in test mode. Please do not perform any real purchases as your order might be deleted without notice." %}
</strong></p>
@@ -137,6 +146,8 @@
</strong></p>
</div>
{% endif %}
{% endif %}
{% if messages %}
{% for message in messages %}
@@ -0,0 +1,40 @@
{% extends "pretixpresale/event/base.html" %}
{% load i18n %}
{% load l10n %}
{% load money %}
{% load eventurl %}
{% load eventsignal %}
{% load thumb %}
{% load rich_text %}
{% load bootstrap3 %}
{% block title %}{% trans "Time machine" %}{% endblock %}
{% block content %}
<div class="panel {% if request.session.timemachine_now_dt %}panel-success{% else %}panel-default{% endif %}">
<div class="panel-heading">
{% trans "Time machine" %}
</div>
<div class="panel-body">
<form action="" method="post" class="">
{% csrf_token %}
{% bootstrap_form_errors timemachine_form %}
<p>{% trans "Test your shop as if it were a different date and time." %}</p>
{% bootstrap_field timemachine_form.now_dt layout="control" show_label=False field_class="col-md-6" %}
<div class="text-right">
<button type="submit" class="btn btn-default btn-lg btn-save">
{% if request.session.timemachine_now_dt %}{% trans "Change" %}{% else %}{% trans "Enable time machine" %}{% endif %}
</button>
{% if request.session.timemachine_now_dt %}
<button name="timemachine_disable" value="true" type="submit" class="btn btn-default btn-lg btn-save">
{% trans "Disable" %}
</button>
{% endif %}
</div>
</form>
<div class="clear"></div>
</div>
</div>
{% endblock %}
+2
View File
@@ -173,6 +173,8 @@ event_patterns = [
re_path(r'^(?P<subevent>\d+)/widget/product_list$', pretix.presale.views.widget.WidgetAPIProductList.as_view(),
name='event.widget.productlist'),
re_path(r'timemachine/$', pretix.presale.views.event.EventTimeMachine.as_view(), name='event.timemachine'),
# Account management is done on org level, but we at least need a logout
re_path(r'^account/logout$', pretix.presale.views.customer.LogoutView.as_view(), name='organizer.customer.logout'),
]
+16 -2
View File
@@ -38,6 +38,9 @@ from importlib import import_module
from urllib.parse import urljoin
from django.conf import settings
from django.contrib.auth import (
BACKEND_SESSION_KEY, SESSION_KEY, get_user_model, load_backend,
)
from django.db.models import Q
from django.http import Http404, HttpResponseForbidden
from django.middleware.csrf import rotate_token
@@ -218,6 +221,15 @@ def customer_logout(request):
request._cached_customer = None
def _apply_user_from_session_data(request, sessiondata):
user_id = get_user_model()._meta.pk.to_python(sessiondata[SESSION_KEY])
backend_path = sessiondata[BACKEND_SESSION_KEY]
if backend_path in settings.AUTHENTICATION_BACKENDS:
backend = load_backend(backend_path)
user = backend.get_user(user_id)
request.user = user
@scope(organizer=None)
def _detect_event(request, require_live=True, require_plugin=None):
@@ -303,7 +315,7 @@ def _detect_event(request, require_live=True, require_plugin=None):
# Restrict locales to the ones available for this event
LocaleMiddleware(NotImplementedError).process_request(request)
if require_live and not request.event.live:
if require_live and (request.event.testmode or not request.event.live):
can_access = (
url.url_name == 'event.auth'
or (
@@ -320,8 +332,10 @@ def _detect_event(request, require_live=True, require_plugin=None):
pass
else:
can_access = 'event_access' in parentdata
if can_access:
_apply_user_from_session_data(request, parentdata)
if not can_access:
if not can_access and not request.event.live:
# Directly construct view instead of just calling `raise` since this case is so common that we
# don't want it to show in our log files.
template = loader.get_template("pretixpresale/event/offline.html")
+47 -2
View File
@@ -42,18 +42,21 @@ from importlib import import_module
from urllib.parse import urlencode
import isoweek
from dateutil import parser
from django import forms
from django.conf import settings
from django.contrib import messages
from django.core.exceptions import PermissionDenied
from django.db.models import (
Count, Exists, IntegerField, OuterRef, Prefetch, Q, Value,
)
from django.db.models.lookups import Exact
from django.http import Http404, HttpResponse
from django.shortcuts import get_object_or_404, render
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.decorators import method_decorator
from django.utils.formats import get_format
from django.utils.functional import SimpleLazyObject
from django.utils.timezone import now
from django.utils.timezone import get_current_timezone, now
from django.utils.translation import gettext_lazy as _, pgettext_lazy
from django.views import View
from django.views.decorators.csrf import csrf_exempt
@@ -78,6 +81,8 @@ from pretix.presale.views.organizer import (
filter_qs_by_attr, has_before_after, weeks_for_template,
)
from ...base.forms.widgets import SplitDateTimePickerWidget
from ...control.permissions import EventPermissionRequiredMixin
from ...helpers.formats.en.formats import SHORT_MONTH_DAY_FORMAT, WEEK_FORMAT
from ...helpers.http import redirect_to_url
from . import (
@@ -918,3 +923,43 @@ class EventAuth(View):
request.session['pretix_event_access_{}'.format(request.event.pk)] = parent
return redirect_to_url(eventreverse(request.event, 'presale:event.index'))
class TimemachineForm(forms.Form):
now_dt = forms.SplitDateTimeField(
label=_('Fake date time'),
widget=SplitDateTimePickerWidget(),
initial=lambda: now().astimezone(get_current_timezone())
)
class EventTimeMachine(EventViewMixin, EventPermissionRequiredMixin, TemplateView):
permission = 'can_change_event_settings'
template_name = 'pretixpresale/event/timemachine.html'
def setup(self, request, *args, **kwargs):
super().setup(request, *args, **kwargs)
self.timemachine_form = TimemachineForm(
data=request.method == 'POST' and request.POST or None,
initial=(
{'now_dt': parser.parse(request.session.get('timemachine_now_dt', None))}
if request.session.get('timemachine_now_dt', None) else {}
)
)
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['timemachine_form'] = self.timemachine_form
return ctx
def post(self, request, *args, **kwargs):
if request.POST.get("timemachine_disable"):
del request.session['timemachine_now_dt']
messages.success(self.request, _('Time machine disabled!'))
return redirect(self.get_success_url())
elif self.timemachine_form.is_valid():
request.session['timemachine_now_dt'] = str(self.timemachine_form.cleaned_data['now_dt'])
return redirect(eventreverse(request.event, "presale:event.index"))
def get_success_url(self) -> str:
return eventreverse(self.request.event, 'presale:event.timemachine')
@@ -0,0 +1 @@
document.forms[0].submit();