-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
/
Copy path__init__.py
237 lines (190 loc) · 7.93 KB
/
__init__.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# -*- coding: utf-8 -*-
"""
Core views, including the main homepage,
documentation and header rendering, and server errors.
"""
import os
import logging
from urllib.parse import urlparse
from django.conf import settings
from django.http import HttpResponseRedirect, Http404, JsonResponse
from django.shortcuts import render, get_object_or_404, redirect
from django.views.generic import TemplateView
from readthedocs.builds.models import Version
from readthedocs.core.utils.general import wipe_version_via_slugs
from readthedocs.core.resolver import resolve_path
from readthedocs.core.symlink import PrivateSymlink, PublicSymlink
from readthedocs.core.views.serve import _serve_file
from readthedocs.projects.constants import PRIVATE
from readthedocs.projects.models import HTMLFile, Project
from readthedocs.redirects.utils import (
get_redirect_response,
project_and_path_from_request,
language_and_version_from_path
)
log = logging.getLogger(__name__)
class NoProjectException(Exception):
pass
class HomepageView(TemplateView):
template_name = 'homepage.html'
def get_context_data(self, **kwargs):
"""Add latest builds and featured projects."""
context = super().get_context_data(**kwargs)
context['featured_list'] = Project.objects.filter(featured=True)
context['projects_count'] = Project.objects.count()
return context
class SupportView(TemplateView):
template_name = 'support.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
support_email = getattr(settings, 'SUPPORT_EMAIL', None)
if not support_email:
support_email = 'support@{domain}'.format(
domain=getattr(
settings,
'PRODUCTION_DOMAIN',
'readthedocs.org',
),
)
context['support_email'] = support_email
return context
def random_page(request, project_slug=None): # pylint: disable=unused-argument
html_file = HTMLFile.objects.order_by('?')
if project_slug:
html_file = html_file.filter(project__slug=project_slug)
html_file = html_file.first()
if html_file is None:
raise Http404
url = html_file.get_absolute_url()
return HttpResponseRedirect(url)
def wipe_version(request, project_slug, version_slug):
version = get_object_or_404(
Version,
project__slug=project_slug,
slug=version_slug,
)
# We need to check by ``for_admin_user`` here to allow members of the
# ``Admin`` team (which doesn't own the project) under the corporate site.
if version.project not in Project.objects.for_admin_user(user=request.user):
raise Http404('You must own this project to wipe it.')
if request.method == 'POST':
wipe_version_via_slugs(
version_slug=version_slug,
project_slug=project_slug
)
return redirect('project_version_list', project_slug)
return render(
request,
'wipe_version.html',
{'version': version, 'project': version.project},
)
def server_error_500(request, template_name='500.html'):
"""A simple 500 handler so we get media."""
r = render(request, template_name)
r.status_code = 500
return r
def server_error_404(request, exception=None, template_name='404.html'): # pylint: disable=unused-argument # noqa
"""
A simple 404 handler so we get media.
.. note::
Marking exception as optional to make /404/ testing page to work.
"""
response = get_redirect_response(request, full_path=request.get_full_path())
# Return a redirect response if there is one
if response:
if response.url == request.build_absolute_uri():
# check that we do have a response and avoid infinite redirect
log.warning(
'Infinite Redirect: FROM URL is the same than TO URL. url=%s',
response.url,
)
else:
return response
# Try to serve custom 404 pages if it's a subdomain/cname
if getattr(request, 'subdomain', False) or getattr(request, 'cname', False):
return server_error_404_subdomain(request, template_name)
# Return the default 404 page generated by Read the Docs
r = render(request, template_name)
r.status_code = 404
return r
def server_error_404_subdomain(request, template_name='404.html'):
"""
Handler for 404 pages on subdomains.
Check if the project associated has a custom ``404.html`` and serve this
page. First search for a 404 page in the current version, then continues
with the default version and finally, if none of them are found, the Read
the Docs default page (Maze Found) is rendered by Django and served.
"""
def resolve_404_path(project, version_slug=None, language=None):
"""
Helper to resolve the path of ``404.html`` for project.
The resolution is based on ``project`` object, version slug and
language.
:returns: tuple containing the (basepath, filename)
:rtype: tuple
"""
filename = resolve_path(
project,
version_slug=version_slug,
language=language,
filename='404.html',
subdomain=True, # subdomain will make it a "full" path without a URL prefix
)
# This breaks path joining, by ignoring the root when given an "absolute" path
if filename[0] == '/':
filename = filename[1:]
version = None
if version_slug:
version_qs = project.versions.filter(slug=version_slug)
if version_qs.exists():
version = version_qs.first()
private = any([
version and version.privacy_level == PRIVATE,
not version and project.privacy_level == PRIVATE,
])
if private:
symlink = PrivateSymlink(project)
else:
symlink = PublicSymlink(project)
basepath = symlink.project_root
fullpath = os.path.join(basepath, filename)
return (basepath, filename, fullpath)
project, full_path = project_and_path_from_request(request, request.get_full_path())
if project:
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)
# Firstly, attempt to serve the 404 of the current version (version_slug)
# Secondly, try to serve the 404 page for the default version (project.get_default_version())
for slug in (version_slug, project.get_default_version()):
basepath, filename, fullpath = resolve_404_path(project, slug, language)
if os.path.exists(fullpath):
log.debug(
'serving 404.html page current version: [project: %s] [version: %s]',
project.slug,
slug,
)
r = _serve_file(request, filename, basepath)
r.status_code = 404
return r
# Finally, return the default 404 page generated by Read the Docs
r = render(request, template_name)
r.status_code = 404
return r
def do_not_track(request):
dnt_header = request.META.get('HTTP_DNT')
# https://w3c.github.io/dnt/drafts/tracking-dnt.html#status-representation
return JsonResponse( # pylint: disable=redundant-content-type-for-json-response
{
'policy': 'https://docs.readthedocs.io/en/latest/privacy-policy.html',
'same-party': [
'readthedocs.org',
'readthedocs.com',
'readthedocs.io', # .org Documentation Sites
'readthedocs-hosted.com', # .com Documentation Sites
],
'tracking': 'N' if dnt_header == '1' else 'T',
}, content_type='application/tracking-status+json',
)