Time machine mode [Z#23129725] (#3961)

Allows organizers to test their shop as if it were a different date and time.

Implemented using a time_machine_now() function which is used instead of regular now(), which can overlay the real date time with a value from a ContextVar, assigned from a session value in EventMiddleware.

For more information, see doc/development/implementation/timemachine.rst

---------

Co-authored-by: Richard Schreiber <schreiber@rami.io>
Co-authored-by: Raphael Michel <michel@rami.io>
This commit is contained in:
Mira
2024-05-17 10:52:17 +02:00
committed by GitHub
parent bfcca7046a
commit b638c00952
38 changed files with 789 additions and 142 deletions

View File

@@ -27,7 +27,7 @@ from django.urls import NoReverseMatch
from django.utils.encoding import smart_str
from django.utils.html import conditional_escape
from pretix.multidomain.urlreverse import build_absolute_uri
from pretix.multidomain.urlreverse import build_absolute_uri, mainreverse
register = template.Library()
@@ -45,11 +45,13 @@ class EventURLNode(URLNode):
for k, v in self.kwargs.items()
}
view_name = self.view_name.resolve(context)
event = self.event.resolve(context)
event = self.event.resolve(context) if self.event is not False else False
url = ''
try:
if self.absolute:
url = build_absolute_uri(event, view_name, kwargs=kwargs)
elif self.event is False:
url = mainreverse(view_name, kwargs)
else:
url = eventreverse(event, view_name, kwargs=kwargs)
except NoReverseMatch:
@@ -65,21 +67,34 @@ class EventURLNode(URLNode):
return url
@register.tag
def eventurl(parser, token, absolute=False):
def multidomainurl(parser, token, has_event, absolute):
"""
Similar to {% url %} in the same way that eventreverse() is similar to reverse().
Similar to {% url %}, but multidomain-aware. Used by eventurl, abseventurl and absmainurl.
Takes an event or organizer object, an url name and optional keyword arguments
If has_event=True, takes an event or organizer object as first template tag parameter.
Always takes an url name and optional keyword arguments after that.
Returns an absolute URL in the following cases:
- absolute=True
- has_event=True and the event has a custom domain
Returns a relative URL otherwise.
"""
bits = token.split_contents()
if len(bits) < 3:
raise TemplateSyntaxError("'%s' takes at least two arguments, an event and the name of a url()." % bits[0])
viewname = parser.compile_filter(bits[2])
event = parser.compile_filter(bits[1])
tagname = bits[0]
if has_event:
if len(bits) < 3:
raise TemplateSyntaxError("'%s' takes at least two arguments, an event and the name of a url()." % tagname)
viewname = parser.compile_filter(bits[2])
event = parser.compile_filter(bits[1])
bits = bits[3:]
else:
if len(bits) < 2:
raise TemplateSyntaxError("'%s' takes at least one arguments, the name of a url()." % tagname)
viewname = parser.compile_filter(bits[1])
event = False
bits = bits[2:]
kwargs = {}
asvar = None
bits = bits[3:]
if len(bits) >= 2 and bits[-2] == 'as':
asvar = bits[-1]
bits = bits[:-2]
@@ -88,16 +103,26 @@ def eventurl(parser, token, absolute=False):
for bit in bits:
match = kwarg_re.match(bit)
if not match:
raise TemplateSyntaxError("Malformed arguments to eventurl tag")
raise TemplateSyntaxError("Malformed arguments to %s tag" % tagname)
name, value = match.groups()
if name:
kwargs[name] = parser.compile_filter(value)
else:
raise TemplateSyntaxError('Event urls only have keyword arguments.')
raise TemplateSyntaxError('Multidomain urls only have keyword arguments.')
return EventURLNode(event, viewname, kwargs, asvar, absolute)
@register.tag
def eventurl(parser, token):
"""
Similar to {% url %} in the same way that eventreverse() is similar to reverse().
Takes an event or organizer object, an url name and optional keyword arguments
"""
return multidomainurl(parser, token, has_event=True, absolute=False)
@register.tag
def abseventurl(parser, token):
"""
@@ -105,4 +130,12 @@ def abseventurl(parser, token):
Returns an absolute URL.
"""
return eventurl(parser, token, absolute=True)
return multidomainurl(parser, token, has_event=True, absolute=True)
@register.tag
def absmainurl(parser, token):
"""
Like {% url %}, but always returns an absolute URL on the main domain.
"""
return multidomainurl(parser, token, has_event=False, absolute=True)

View File

@@ -180,7 +180,7 @@ def build_absolute_uri(obj, urlname, kwargs=None):
"""
Works similar to ``eventreverse`` but always returns an absolute URL.
:param obj: An ``Event`` or ``Organizer`` object
:param obj: An ``Event`` or ``Organizer`` object, or ``False`` to generate main domain URLs
:param name: The name of the URL route
:type name: str
:param kwargs: A dictionary of additional keyword arguments that should be used. You do not
@@ -188,7 +188,10 @@ def build_absolute_uri(obj, urlname, kwargs=None):
needed.
:returns: An absolute URL (including scheme and host) as a string
"""
reversedurl = eventreverse(obj, urlname, kwargs)
if obj is False:
reversedurl = mainreverse(urlname, kwargs)
else:
reversedurl = eventreverse(obj, urlname, kwargs)
if '://' in reversedurl:
return reversedurl
return urljoin(settings.SITE_URL, reversedurl)