Skip to content

Contextualize 404 page #9657

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 97 commits into from
Apr 26, 2023
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
97 commits
Select commit Hold shift + click to select a range
fd380a5
WIP: Adds context to 404 page
benjaoming Oct 11, 2022
ad1cb2f
Merge branch 'main' of github.com:readthedocs/readthedocs.org into co…
benjaoming Oct 12, 2022
aca278d
WIP: Adds a test 404 page
benjaoming Oct 12, 2022
7f86767
Contextualization gives us 3 interesting scenarios for the 404 page
benjaoming Oct 12, 2022
31ec5f5
404 handler: Receive exception keyword
benjaoming Oct 12, 2022
8cbff08
Improve contextualized messages, not ready for review, needs further …
benjaoming Oct 27, 2022
bc73398
Remove search form, that was a bad idea, since it will only work on s…
benjaoming Nov 1, 2022
a8573f9
Early work on an approach that uses a custom Http404 exception to sav…
benjaoming Nov 2, 2022
8bd53b6
Refactor error templates to separate contextualized handling + some t…
benjaoming Nov 3, 2022
7d1c69a
Apply new exceptions in some of the cases
benjaoming Nov 4, 2022
a30fa3c
Update readthedocs/proxito/exceptions.py
benjaoming Nov 7, 2022
c550ade
Merge branch 'main' of github.com:readthedocs/readthedocs.org into co…
benjaoming Nov 23, 2022
0ef9c4d
Merge branch 'contextualize-404' of github.com:benjaoming/readthedocs…
benjaoming Nov 23, 2022
c505544
Refactor unresolver
stsewd Feb 16, 2023
d2eadec
Merge branch 'main' into refactor-unresolver
stsewd Feb 16, 2023
14bdbd2
Move external version checks inside unresolver
stsewd Feb 21, 2023
b807b2d
Merge branch 'main' into refactor-unresolver
stsewd Feb 21, 2023
b5006dc
Merge branch 'main' into refactor-unresolver
stsewd Feb 21, 2023
34d0cea
Merge branch 'main' of github.com:readthedocs/readthedocs.org into co…
benjaoming Feb 22, 2023
dd43631
!fixup 8cbff084845a9a7ad0fd660f96be8bab245ec171
benjaoming Feb 22, 2023
0c07025
Update link to Custom 404 docs, remove rediraffe from tips
benjaoming Feb 22, 2023
9367e9e
Apply suggestions from code review
benjaoming Feb 22, 2023
d38491e
Raise custom 404 if subproject isn't found
benjaoming Feb 22, 2023
33f685b
Merge branch 'contextualize-404' of github.com:benjaoming/readthedocs…
benjaoming Feb 22, 2023
7f6f1b7
WIP: Conclude structure of exception classes and templates, needs mor…
benjaoming Feb 22, 2023
36b9f02
Updates from review
stsewd Feb 22, 2023
a4dee70
Reuse 404 view between proxito and general app, remove debug logging
benjaoming Feb 23, 2023
797e96b
Removes log.debug in views.serve
benjaoming Feb 23, 2023
8d38b8f
More work on text copy, fix bugs, raise ProxitoProjectVersionHttp404 …
benjaoming Feb 23, 2023
b60a109
Contextualize when subprojects aren't found. The disadvantages of the…
benjaoming Feb 23, 2023
b557590
Clean up
benjaoming Feb 23, 2023
3f7f2fd
Add project:slug search scope when pages aren't found
benjaoming Feb 23, 2023
63e64c2
clean up
benjaoming Feb 23, 2023
18823ba
Missed some linting there
benjaoming Feb 23, 2023
6dfde4c
fix pylint errors
benjaoming Feb 23, 2023
47338e9
Merge branch 'main' into refactor-unresolver
stsewd Feb 27, 2023
38496bc
Proxito: use unresolved in 404 handler
stsewd Feb 27, 2023
9fabc3d
Merge branch 'main' of github.com:readthedocs/readthedocs.org into co…
benjaoming Feb 28, 2023
bece466
Don't use request.path as the "not found" path for proxito 404 except…
benjaoming Feb 28, 2023
46496df
Merge branch 'main' into refactor-unresolver
stsewd Feb 28, 2023
69f4886
Updates from review
stsewd Feb 28, 2023
b25608a
Merge remote-tracking branch 'upstream/use-proxito-v2-in-404-handler'…
benjaoming Mar 1, 2023
2184112
Missing imports
benjaoming Mar 1, 2023
ce28e12
Linting: Too many blank lines
benjaoming Mar 1, 2023
a9e7b09
Merge branch 'use-proxito-v2-in-404-handler' into contextualize-404
benjaoming Mar 1, 2023
7e12e2e
Merge branch 'main' of github.com:readthedocs/readthedocs.org into co…
benjaoming Mar 1, 2023
3c8b76b
Merge branch 'main' of github.com:readthedocs/readthedocs.org into us…
benjaoming Mar 1, 2023
29a560c
Merge branch 'refactor-unresolver' of github.com:readthedocs/readthed…
benjaoming Mar 1, 2023
01f8cee
Add exception type for new 404 translation pages
benjaoming Mar 1, 2023
3085d95
Merge branch 'main' into use-proxito-v2-in-404-handler
stsewd Mar 2, 2023
05384be
Remove duplicated imports
stsewd Mar 2, 2023
7af491a
Black
stsewd Mar 2, 2023
86f67b2
Fix exc
stsewd Mar 2, 2023
2d92061
Update tests
stsewd Mar 2, 2023
f5b2db0
Merge branch 'main' into use-proxito-v2-in-404-handler
stsewd Mar 2, 2023
c017503
Move check to new implementation only
stsewd Mar 2, 2023
4c15d16
Merge branch 'use-proxito-v2-in-404-handler' of github.com:readthedoc…
benjaoming Mar 3, 2023
b5d96f4
Remove double error message for project 404s
benjaoming Mar 3, 2023
a36eb90
Add missing kwarg proxito_path for subproject 404 exception
benjaoming Mar 3, 2023
be22848
Updates from review
stsewd Mar 7, 2023
7e02a55
Merge branch 'use-proxito-v2-in-404-handler' of github.com:readthedoc…
benjaoming Mar 7, 2023
e3ff836
Add a new 404 exception class and targeted template for missing proje…
benjaoming Mar 7, 2023
03bbc6d
Updates from review
stsewd Mar 7, 2023
5ae6b6d
Merge branch 'use-proxito-v2-in-404-handler' of github.com:readthedoc…
benjaoming Mar 8, 2023
af07796
Merge branch 'main' of github.com:readthedocs/readthedocs.org into co…
benjaoming Mar 8, 2023
d597b09
Merge branch 'main' of github.com:readthedocs/readthedocs.org into co…
benjaoming Apr 11, 2023
b98dd0b
WIP: Simplify contextualized 404 messages into a set of exceptions. T…
benjaoming Apr 11, 2023
9a366bb
Revert changes on old proxito decorators implementation
benjaoming Apr 11, 2023
651161e
WIP: refactor
benjaoming Apr 12, 2023
3b7f4c0
Remove custom HTTP responses from most old proxito code, include CNAM…
benjaoming Apr 12, 2023
7c0c00c
Remove more changes to old proxito code
benjaoming Apr 12, 2023
93d7c48
Remove more old proxito implementation changes
benjaoming Apr 12, 2023
a1361d4
Use PRODUCTION_DOMAIN for search form action
benjaoming Apr 12, 2023
d20187c
Remove custom 404 handler from general app, have only that logic in p…
benjaoming Apr 12, 2023
b0e8db4
Remove a couple of additional unused patterns
benjaoming Apr 12, 2023
f0747c5
Comment accuracy update
benjaoming Apr 12, 2023
883aa50
Update another comment
benjaoming Apr 12, 2023
e70cec4
Revert DEBUG=False
benjaoming Apr 12, 2023
f195419
Move exceptions from core to proxito + use "path" from 404 serve view…
benjaoming Apr 12, 2023
62df795
Rename a few things
benjaoming Apr 12, 2023
11d099a
Removed unused kwarg
benjaoming Apr 12, 2023
2fa8a52
Update test cases to check for expected 404 exception and HTTP status
benjaoming Apr 12, 2023
92abfb9
Adds a test case variant to check the exception handling stack
benjaoming Apr 12, 2023
084ccc7
Apply suggestions from @stsewd code review
benjaoming Apr 19, 2023
607c735
use pgettext with context for 404 subjects
benjaoming Apr 19, 2023
344a845
change kwarg name, use some explicit kwargs
benjaoming Apr 19, 2023
6156040
Fix imports to be absolute
benjaoming Apr 19, 2023
9799f7e
Remove h3 from search bar when included with a search term
benjaoming Apr 19, 2023
239a0af
Merge branch 'main' of github.com:readthedocs/readthedocs.org into co…
benjaoming Apr 19, 2023
81e46a9
explicitly require certain exception arguments, trim the ones we're n…
benjaoming Apr 20, 2023
d73d397
Apply suggestions from @stsewd code review
benjaoming Apr 26, 2023
18b5788
Remove |default filter and assume a value exists
benjaoming Apr 26, 2023
8945bff
Update readthedocs/templates/errors/404/include_tips.html
benjaoming Apr 26, 2023
a4816e4
Merge branch 'contextualize-404' of github.com:benjaoming/readthedocs…
benjaoming Apr 26, 2023
5e9017e
Add more context for translators of not_found_subject
benjaoming Apr 26, 2023
f5cf9d9
Merge branch 'main' of github.com:readthedocs/readthedocs.org into co…
benjaoming Apr 26, 2023
8cd0404
Merge branch 'main' of github.com:readthedocs/readthedocs.org into co…
benjaoming Apr 26, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions readthedocs/core/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,26 @@ def get_context_data(self, **kwargs):
return context


def server_error_404(request, template_name="404.html", exception=None):
"""A simple 404 handler."""
# This property is set by ProxitoHttp404. We could also have a look at the
# subproject_slug
project_slug = getattr(exception, "project_slug", None)
log.debug(exception)
project = getattr(exception, "project", None)
log.debug(
"404 page detected a project slug in request.",
project_slug=project_slug,
)
r = render(
request,
template_name,
context={"project": project, "project_slug": project_slug},
)
r.status_code = 404
return r


def server_error_500(request, template_name='500.html'):
"""A simple 500 handler so we get media."""
r = render(request, template_name)
Expand Down
9 changes: 9 additions & 0 deletions readthedocs/proxito/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from django.http.response import Http404


class ProxitoHttp404(Http404):
def __init__(self, message, project_slug=None, project=None, subproject_slug=None):
self.project_slug = project_slug
self.project = project
self.subproject_slug = subproject_slug
super().__init__(message)
16 changes: 12 additions & 4 deletions readthedocs/proxito/views/decorators.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import structlog
from functools import wraps

from django.http import Http404
import structlog

from readthedocs.projects.models import Project, ProjectRelationship
from readthedocs.proxito.exceptions import ProxitoHttp404

log = structlog.get_logger(__name__) # noqa

Expand Down Expand Up @@ -44,7 +44,9 @@ def inner_view( # noqa
subproject_slug=subproject_slug,
project_slug=kwargs['project'].slug,
)
raise Http404('Invalid subproject slug')
raise ProxitoHttp404(
"Invalid subproject slug", subproject_slug=subproject_slug
)
return view_func(request, subproject=subproject, *args, **kwargs)

return inner_view
Expand Down Expand Up @@ -74,7 +76,13 @@ def inner_view( # noqa
try:
project = Project.objects.get(slug=project_slug)
except Project.DoesNotExist:
raise Http404('Project does not exist.')
log.debug(
"Project not found.",
project_slug=project_slug,
)
raise ProxitoHttp404(
"Project does not exist.", project_slug=project_slug
)
return view_func(request, project=project, *args, **kwargs)

return inner_view
29 changes: 22 additions & 7 deletions readthedocs/proxito/views/serve.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import structlog
from django.conf import settings
from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from django.urls import resolve as url_resolve
from django.utils.decorators import method_decorator
Expand All @@ -22,6 +22,7 @@
from readthedocs.projects.constants import SPHINX_HTMLDIR
from readthedocs.projects.models import Feature
from readthedocs.projects.templatetags.projects_tags import sort_version_aware
from readthedocs.proxito.exceptions import ProxitoHttp404
from readthedocs.redirects.exceptions import InfiniteRedirectException
from readthedocs.storage import build_media_storage, staticfiles_storage

Expand Down Expand Up @@ -109,7 +110,7 @@ def get(self,
# version on the database we want to return 404.
if (version and not version.active) or (version_slug and not version):
log.warning("Version does not exist or is not active.")
raise Http404("Version does not exist or is not active.")
raise ProxitoHttp404("Version does not exist or is not active.")

if self._is_cache_enabled(final_project) and version and not version.is_private:
# All public versions can be cached.
Expand Down Expand Up @@ -174,7 +175,7 @@ def get(self,
'Invalid URL for project with versions.',
filename=filename,
)
raise Http404('Invalid URL for project with versions')
raise ProxitoHttp404("Invalid URL for project with versions")

redirect_path, http_status = self.get_redirect(
project=final_project,
Expand Down Expand Up @@ -261,17 +262,25 @@ def get(self, request, proxito_path, template_name='404.html'):
proxito_path,
urlconf='readthedocs.proxito.urls',
)
log.debug("Resolved a URL.")

version_slug = kwargs.get('version_slug')
version_slug = self.get_version_from_host(request, version_slug)
final_project, lang_slug, version_slug, filename = _get_project_data_from_request( # noqa
log.debug("Getting _get_project_data_from_request.")
(
final_project,
lang_slug,
version_slug,
filename,
) = _get_project_data_from_request( # noqa
request,
project_slug=kwargs.get('project_slug'),
subproject_slug=kwargs.get('subproject_slug'),
lang_slug=kwargs.get('lang_slug'),
version_slug=version_slug,
filename=kwargs.get('filename', ''),
)
log.debug("Finished _get_project_data_from_request.")

log.bind(
project_slug=final_project.slug,
Expand Down Expand Up @@ -390,7 +399,13 @@ def get(self, request, proxito_path, template_name='404.html'):
path=filename,
full_path=proxito_path,
)
raise Http404('No custom 404 page found.')
log.debug("Raising ProxitoHttp404")
raise ProxitoHttp404(
"No custom 404 page found.",
project=final_project,
project_slug=kwargs.get("project_slug"),
subproject_slug=kwargs.get("subproject_slug"),
)

def _register_broken_link(self, project, version, path, full_path):
try:
Expand Down Expand Up @@ -472,7 +487,7 @@ def get(self, request, project):

if no_serve_robots_txt:
# ... we do return a 404
raise Http404()
raise ProxitoHttp404()

storage_path = project.get_storage_path(
type_='html',
Expand Down Expand Up @@ -595,7 +610,7 @@ def changefreqs_generator():
only_active=True,
)
if not public_versions.exists():
raise Http404
raise ProxitoHttp404()

sorted_versions = sort_version_aware(public_versions)

Expand Down
40 changes: 33 additions & 7 deletions readthedocs/proxito/views/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

import structlog
from django.http import HttpResponse
from django.shortcuts import get_object_or_404, render
from django.shortcuts import render

from readthedocs.projects.models import Project
from readthedocs.proxito.exceptions import ProxitoHttp404

from .decorators import map_project_slug, map_subproject_slug

Expand Down Expand Up @@ -30,11 +33,23 @@ def proxito_404_page_handler(request, exception=None, template_name='404.html'):
"""

if request.resolver_match and request.resolver_match.url_name != 'proxito_404_handler':
log.debug("Displaying a 'fast 404'")
return fast_404(request, exception, template_name)

resp = render(request, template_name)
resp.status_code = 404
return resp
project_slug = getattr(exception, "project_slug", None)
log.debug(exception)
project = getattr(exception, "project", None)
log.debug(
"404 page detected a project slug in request.",
project_slug=project_slug,
)
r = render(
request,
template_name,
context={"project": project, "project_slug": project_slug},
)
r.status_code = 404
return r


@map_project_slug
Expand Down Expand Up @@ -70,9 +85,20 @@ def _get_project_data_from_request(
if not lang_slug or lang_slug == current_project.language:
final_project = current_project
else:
final_project = get_object_or_404(
current_project.translations.all(), language=lang_slug
)
try:
final_project = current_project.translations.get(language=lang_slug)
except Project.DoesNotExist:
log.debug(
"Raising 404 for language slug",
lang_slug=lang_slug,
project_slug=current_project.slug,
)
raise ProxitoHttp404(
"Did not find translation",
project=current_project,
project_slug=current_project.slug,
subproject_slug=lang_slug,
)

# Handle single version by grabbing the default version
# We might have version_slug when we're serving a PR
Expand Down
4 changes: 2 additions & 2 deletions readthedocs/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class CommunityBaseSettings(Settings):
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'

# Debug settings
DEBUG = True
DEBUG = False
RTD_FORCE_SHOW_DEBUG_TOOLBAR = False

@property
Expand Down Expand Up @@ -73,7 +73,7 @@ def SHOW_DEBUG_TOOLBAR(self):
PRODUCTION_DOMAIN = 'readthedocs.org'
PUBLIC_DOMAIN = None
PUBLIC_DOMAIN_USES_HTTPS = False
USE_SUBDOMAIN = False
USE_SUBDOMAIN = True
PUBLIC_API_URL = 'https://{}'.format(PRODUCTION_DOMAIN)
RTD_INTERSPHINX_URL = 'https://{}'.format(PRODUCTION_DOMAIN)
RTD_EXTERNAL_VERSION_DOMAIN = 'external-builds.readthedocs.io'
Expand Down
42 changes: 41 additions & 1 deletion readthedocs/templates/404.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{% load i18n %}

{% block title %}
{% trans "Maze Found" %}
{% trans "404 - Maze Found" %}
{% endblock %}

{% block header-wrapper %}
Expand All @@ -16,6 +16,46 @@
{% block language-select-form %}{% endblock %}

{% block content %}

<h1>{% trans "404 - page not found" %}</h1>

{% if project %}

{# project exists #}

<p>
{% blocktrans with project_slug=project.slug request_path=request.path %}You are browsing the documentation of "{{ project_slug }}". The page you are looking for at <code>{{ request.path }}</code> was not found.{% endblocktrans %}
</p>

<p>
{% blocktrans %}Documentation changes over time. Pages are moved around. You can try to navigate to <a href="/">the index page</a> of the project and use its navigation or search function to find a similar page.{% endblocktrans %}</p>

<h3>{% trans "Are you the project owner?" %}</h3>

<p>{% trans "Here are some tips to address 404 errors:" %}</p>

<ul>
<li>{% trans "Use custom 404 pages:" %} <a href="https://docs.readthedocs.io/en/latest/hosting.html#custom-not-found-404-pages">{% trans "Read more" %}»</a></li>
<li>{% trans "Create redirects when you move contents:" %} <a href="https://docs.readthedocs.io/en/stable/user-defined-redirects.html">{% trans "Read more" %}»</a></li>
<li>{% trans "Use rediraffe to manage redirects in Sphinx:" %} <a href="https://pypi.org/project/sphinxext-rediraffe/">{% trans "Read more" %}»</a></li>
</ul>

{% elif "_proxito_404_" in request.path %}

{# we are looking at a 404 generated by proxito, we then know that the project itself doesn't exist or hasn't been built #}

<p>
{% blocktrans with project_slug=project_slug %}The project "{{ project_slug }}" that you are looking for either does not exist, has been deleted or does not yet have any valid builds.{% endblocktrans %}
</p>

{% else %}

<p>{% trans "You have encountered a 404 on Read the Docs. You are either looking for a page that does not exist or a project that has been removed." %}</p>

<p>{% trans "If you believe this is a technical error, please contact our support:" %} <a href="{% url 'support' %}">{% trans "Go to support" %}»</a></p>

{% endif %}

<pre style="line-height: 1.25; white-space: pre;">

\ SORRY /
Expand Down
3 changes: 3 additions & 0 deletions readthedocs/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@
HomepageView,
SupportView,
do_not_track,
server_error_404,
server_error_500,
)
from readthedocs.search.views import GlobalSearchView

admin.autodiscover()

handler404 = server_error_404
handler500 = server_error_500

basic_urls = [
Expand Down Expand Up @@ -128,6 +130,7 @@
document_root=os.path.join(settings.MEDIA_ROOT, build_format),
)
debug_urls += [
path("404/", server_error_404),
path(
"style-catalog/",
TemplateView.as_view(template_name="style_catalog.html"),
Expand Down