95 lines
2.8 KiB
Python
95 lines
2.8 KiB
Python
"""
|
|
Redirection view support.
|
|
|
|
This module allows for parsing a URL path, looking up redirects associated
|
|
with it in the database, and generating a redirect response.
|
|
|
|
These are not used directly as views; they are instead included into 404
|
|
handlers, so that redirects only take effect if no other view matches.
|
|
"""
|
|
import logging
|
|
import re
|
|
from urllib.parse import urlparse, urlunparse
|
|
|
|
from django.http import HttpResponseRedirect
|
|
|
|
from readthedocs.constants import LANGUAGES_REGEX
|
|
from readthedocs.projects.models import Project
|
|
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
def project_and_path_from_request(request, path):
|
|
"""
|
|
Parse the project from a request path.
|
|
|
|
Return a tuple (project, path) where `project` is a projects.Project if
|
|
a matching project exists, and `path` is the unmatched remainder of the
|
|
path.
|
|
|
|
If the path does not match, or no matching project is found, then `project`
|
|
will be ``None``.
|
|
"""
|
|
if hasattr(request, 'slug'):
|
|
project_slug = request.slug
|
|
elif path.startswith('/docs/'):
|
|
# In this case we use the docs without subdomains. So let's strip the
|
|
# docs prefix.
|
|
match = re.match(
|
|
r'^/docs/(?P<project_slug>[^/]+)(?P<path>/.*)$',
|
|
path,
|
|
)
|
|
if match:
|
|
project_slug = match.groupdict()['project_slug']
|
|
path = match.groupdict()['path']
|
|
else:
|
|
return None, path
|
|
else:
|
|
return None, path
|
|
|
|
try:
|
|
project = Project.objects.get(slug=project_slug)
|
|
except Project.DoesNotExist:
|
|
return None, path
|
|
return project, path
|
|
|
|
|
|
def language_and_version_from_path(path):
|
|
match = re.match(
|
|
r'^/(?P<language>%s)/(?P<version_slug>[^/]+)(?P<path>/.*)$' % LANGUAGES_REGEX,
|
|
path,
|
|
)
|
|
if match:
|
|
language = match.groupdict()['language']
|
|
version_slug = match.groupdict()['version_slug']
|
|
path = match.groupdict()['path']
|
|
return language, version_slug, path
|
|
return None, None, path
|
|
|
|
|
|
def get_redirect_response(request, full_path):
|
|
project, full_path = project_and_path_from_request(request, full_path)
|
|
if not project:
|
|
return None
|
|
|
|
language = None
|
|
version_slug = None
|
|
schema, netloc, path, params, query, fragments = urlparse(full_path)
|
|
if not project.single_version:
|
|
language, version_slug, path = language_and_version_from_path(path)
|
|
|
|
path = project.redirects.get_redirect_path(
|
|
path=path, language=language, version_slug=version_slug
|
|
)
|
|
|
|
if path is None:
|
|
return None
|
|
|
|
new_path = urlunparse((schema, netloc, path, params, query, fragments))
|
|
|
|
# Re-use the domain and protocol used in the current request.
|
|
# Redirects shouldn't change the domain, version or language.
|
|
new_path = request.build_absolute_uri(new_path)
|
|
return HttpResponseRedirect(new_path)
|