in kitsune/inproduct/views.py [0:0]
def redirect(request, product, version, platform, locale, topic=None):
"""Redirect in-product URLs to the right place."""
redirects = Redirect.objects.all()
# In order from least to most important.
parts = ("locale", "product", "version", "platform", "topic")
# First we remove any redirects that explicitly don't match. Do this in
# Python to avoid an explosion of cache use.
t = topic if topic else ""
def f(redirect):
matches = (
redirect.product.lower() in (product.lower(), ""),
redirect.version.lower() in (version.lower(), ""),
redirect.platform.lower() in (platform.lower(), ""),
redirect.locale.lower() in (locale.lower(), ""),
redirect.topic.lower() == t.lower(),
)
return all(matches)
redirects = list(filter(f, redirects))
# Assign a ordinal (score) to each redirect based on how specific it is,
# then order by score descending.
#
# Scores are weighted by powers of 2 so that specifying the most important
# field (i.e. topic) outweighs specifying all 4 other fields. This means
# we should find the correct match faster in most common cases.
# For example, if you have two redirects that have topic='foo', and one
# of them also has platform='mac', and one with platform='win', they'll
# sort like:
# 24, Redirect(topic='foo', platform='mac')
# 16, Redirect(topic='foo')
# 8, Redirect(platform='win')
# Macs going to 'foo' will will hit the first redirect. Everyone else
# going to 'foo' will hit the second. (Windows users going to 'foo' would
# never match Redirect(platform='win') but if it sorted higher, it would
# take longer to get there.)
def ordinal(redirect):
score = 0
for i, part in enumerate(parts):
if getattr(redirect, part) != "":
score += 1 << i
return score, redirect
ordered = list(map(ordinal, redirects))
ordered.sort(key=lambda x: x[0], reverse=True)
# A redirect matches if all its fields match. A field matches if it is
# blank or matches the input.
def matches(redirect, **kw):
for part in parts:
attr = getattr(redirect, part)
if attr != "":
v = kw[part] if kw[part] else ""
if attr.lower() != v.lower():
return False
return True
# As soon as we've found a match, that's the best one.
destination = None
for score, redirect in ordered:
if matches(
redirect,
product=product,
version=version,
platform=platform,
locale=locale,
topic=topic,
):
destination = redirect
break
# Oh noes! We didn't find a target.
if not destination:
raise Http404
# If the target starts with HTTP, we don't add a locale or query string
# params.
if destination.target.startswith("http"):
# Preserve all incoming query parameters when redirecting to the target.
target = urlparams(destination.target, query_dict=request.GET)
else:
# Preserve all incoming query parameters except for "as" and "eu".
params = {"as": "u"}
if (
("utm_source" not in request.GET)
and ("?utm_source=" not in destination.target)
and ("&utm_source=" not in destination.target)
):
params["utm_source"] = "inproduct"
if hasattr(request, "eu_build"):
params["eu"] = 1
target = "/%s/%s" % (locale, destination.target.lstrip("/"))
target = urlparams(target, query_dict=request.GET, **params)
# Switch over to HTTPS if we DEBUG=False and sample is active.
if not settings.DEBUG and waffle.sample_is_active("inproduct-https"):
domain = Site.objects.get_current().domain
target = "https://%s%s" % (domain, target)
# 302 because these can change over time.
return HttpResponseRedirect(target)