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 11 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
28 changes: 28 additions & 0 deletions readthedocs/core/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,34 @@ def get_context_data(self, **kwargs):
return context


def server_error_404(
request, template_name="errors/404/community.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)
subproject_slug = getattr(exception, "subproject_slug", None)

if subproject_slug:
template_name = "errors/404/no_subproject.html"
elif project:
template_name = "errors/404/no_project_page.html"
elif project_slug:
template_name = "errors/404/no_project.html"

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
56 changes: 56 additions & 0 deletions readthedocs/proxito/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"""
This module contains exceptions that may be raised in El Proxito application
but are handled elsewhere in the Django project.

--------------
404 exceptions
--------------

Because an Http404 can be raised anywhere in the code,
we want to add more context for targeted error handling and user-facing communication
"""
from django.http.response import Http404


class ProxitoHttp404(Http404):
"""
General error class for proxity 404s
"""

def __init__(self, message):
super().__init__(message)


class ProxitoProjectHttp404(ProxitoHttp404):
"""
Raised if a project was not found
"""

def __init__(self, message, project_slug=None):
self.project_slug = project_slug
super().__init__(message)


class ProxitoSubProjectHttp404(ProxitoProjectHttp404):
"""
Raised if a sub-project was not found
"""
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)


class ProxitoProjectPageHttp404(ProxitoHttp404):
"""
Raised if a page inside an existing project was not found

Note: The containing project can be both a project or a subproject inside another project
"""

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)
19 changes: 15 additions & 4 deletions readthedocs/proxito/views/decorators.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
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 (
ProxitoProjectHttp404,
ProxitoSubProjectHttp404,
)

log = structlog.get_logger(__name__) # noqa

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

return inner_view
Expand Down Expand Up @@ -74,7 +79,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 ProxitoProjectHttp404(
"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, ProxitoProjectPageHttp404
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 ProxitoProjectPageHttp404(
"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
11 changes: 1 addition & 10 deletions readthedocs/templates/401.html
Original file line number Diff line number Diff line change
@@ -1,20 +1,11 @@
{% extends "base.html" %}
{% extends "errors/base.html" %}
{% load core_tags %}
{% load i18n %}

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

{% block header-wrapper %}
{% include "error_header.html" %}
{% endblock %}

{% block notify %}{% endblock %}

{# Hide the language select form so we don't set a CSRF cookie #}
{% block language-select-form %}{% endblock %}

{% block content %}
<h3>Permission Denied</h3>
<p>
Expand Down
46 changes: 2 additions & 44 deletions readthedocs/templates/404.html
Original file line number Diff line number Diff line change
@@ -1,44 +1,2 @@
{% extends "base.html" %}
{% load core_tags %}
{% load i18n %}

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

{% block header-wrapper %}
{% include "error_header.html" %}
{% endblock %}

{% block notify %}{% endblock %}

{# Hide the language select form so we don't set a CSRF cookie #}
{% block language-select-form %}{% endblock %}

{% block content %}
<pre style="line-height: 1.25; white-space: pre;">

\ SORRY /
\ /
\ This page does /
] not exist yet. [ ,'|
] [ / |
]___ ___[ ,' |
] ]\ /[ [ |: |
] ] \ / [ [ |: |
] ] ] [ [ [ |: |
] ] ]__ __[ [ [ |: |
] ] ] ]\ _ /[ [ [ [ |: |
] ] ] ] (#) [ [ [ [ :===='
] ] ]_].nHn.[_[ [ [
] ] ] HHHHH. [ [ [
] ] / `HH("N \ [ [
]__]/ HHH " \[__[
] NNN [
] N/" [
] N H [
/ N \
/ q, \
/ \
</pre>
{% endblock %}
{% extends "errors/404/base.html" %}
{# this is a deprecated template, will be removed. Please use one of the targeted 404s in templates/404/ #}
11 changes: 1 addition & 10 deletions readthedocs/templates/429.html
Original file line number Diff line number Diff line change
@@ -1,20 +1,11 @@
{% extends "base.html" %}
{% extends "errors/base.html" %}
{% load core_tags %}
{% load i18n %}

{% block title %}
{% trans "Too many requests" %}
{% endblock %}

{% block header-wrapper %}
{% include "error_header.html" %}
{% endblock %}

{% block notify %}{% endblock %}

{# Hide the language select form so we don't set a CSRF cookie #}
{% block language-select-form %}{% endblock %}

{% block content %}
<pre style="line-height: 1.25; white-space: pre;">
.--~~,__
Expand Down
Loading