Skip to content

Commit 41b6c16

Browse files
committed
Generate general sitemap.xml for projects
1 parent f06271b commit 41b6c16

File tree

3 files changed

+94
-1
lines changed

3 files changed

+94
-1
lines changed

readthedocs/core/urls/subdomain.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
from readthedocs.core.views.serve import (
1414
redirect_page_with_filename,
15-
redirect_project_slug, serve_docs, robots_txt,
15+
redirect_project_slug, serve_docs, robots_txt, sitemap_xml,
1616
)
1717
from readthedocs.core.views import (
1818
server_error_500,
@@ -25,6 +25,7 @@
2525

2626
subdomain_urls = [
2727
url(r'robots.txt$', robots_txt, name='robots_txt'),
28+
url(r'sitemap.xml$', sitemap_xml, name='sitemap.xml'),
2829

2930
url(r'^(?:|projects/(?P<subproject_slug>{project_slug})/)'
3031
r'page/(?P<filename>.*)$'.format(**pattern_opts),

readthedocs/core/views/serve.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,17 @@
3838
from django.shortcuts import get_object_or_404
3939
from django.shortcuts import render
4040
from django.utils.encoding import iri_to_uri
41+
from django.views.decorators.cache import cache_page
4142
from django.views.static import serve
4243

4344
from readthedocs.builds.models import Version
4445
from readthedocs.core.permissions import AdminPermission
4546
from readthedocs.core.resolver import resolve, resolve_path
4647
from readthedocs.core.symlink import PrivateSymlink, PublicSymlink
4748
from readthedocs.projects import constants
49+
from readthedocs.projects.constants import PRIVATE
4850
from readthedocs.projects.models import Project, ProjectRelationship
51+
from readthedocs.projects.templatetags.projects_tags import sort_version_aware
4952

5053
log = logging.getLogger(__name__)
5154

@@ -269,3 +272,74 @@ def robots_txt(request, project):
269272
return HttpResponse(open(fullpath).read(), content_type='text/plain')
270273

271274
return HttpResponse('User-agent: *\nAllow: /\n', content_type='text/plain')
275+
276+
277+
@map_project_slug
278+
# TODO: make this cache dependent on the project's slug
279+
@cache_page(60 * 60 * 24 * 3) # 3 days
280+
def sitemap_xml(request, project):
281+
282+
def priorities_generator():
283+
"""
284+
Generator returning ``priority`` needed by sitemap.xml.
285+
286+
It generates values from 1 to 0.1 by decreasing in 0.1 on each
287+
iteration. After 0.1 is reached, it will keep returning 0.1.
288+
"""
289+
priorities = [1, 0.9, 0.8, 0.7, 0.6, 0.5, 0.3, 0.2]
290+
for p in priorities:
291+
yield p
292+
293+
while True:
294+
yield 0.1
295+
296+
def changefreqs_generator():
297+
"""
298+
Generator returning ``changefreq`` needed by sitemap.xml.
299+
300+
It returns ``daily`` on first iteration, then ``weekly`` and then it
301+
will return always ``monthly``.
302+
"""
303+
changefreqs = ['daily', 'weekly']
304+
for c in changefreqs:
305+
yield c
306+
307+
while True:
308+
yield 'monthly'
309+
310+
sorted_versions = sort_version_aware(project.versions.filter(active=True))
311+
312+
versions = []
313+
for version, priority, changefreq in zip(
314+
sorted_versions, priorities_generator(), changefreqs_generator()):
315+
element = {
316+
'loc': version.get_subdomain_url(),
317+
'lastmod': version.builds.order_by('-date').first().date.isoformat(),
318+
'priority': priority,
319+
'changefreq': changefreq,
320+
'languages': [],
321+
}
322+
if project.translations.exists():
323+
for translation in project.translations.all():
324+
href = project.get_docs_url(
325+
version_slug=version.slug,
326+
lang_slug=translation.language,
327+
private=version.privacy_level == PRIVATE,
328+
)
329+
element['languages'].append({
330+
'hreflang': translation.language,
331+
'href': href,
332+
})
333+
334+
# add itself also as protocol requires
335+
element['languages'].append({
336+
'hreflang': project.language,
337+
'href': element['loc'],
338+
})
339+
340+
versions.append(element)
341+
342+
context = {
343+
'versions': versions,
344+
}
345+
return render(request, 'sitemap.xml', context, content_type='application/xml')

readthedocs/templates/sitemap.xml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
3+
xmlns:xhtml="http://www.w3.org/1999/xhtml">
4+
{% for version in versions %}
5+
<url>
6+
<loc>{{ version.loc }}</loc>
7+
{% for language in version.languages %}
8+
<xhtml:link
9+
rel="alternate"
10+
hreflang="{{ language.hreflang }}"
11+
href="{{ language.href }}"/>
12+
{% endfor %}
13+
<lastmod>{{ version.lastmod }}</lastmod>
14+
<changefreq>{{ version.changefreq }}</changefreq>
15+
<priority>{{ version.priority }}</priority>
16+
</url>
17+
{% endfor %}
18+
</urlset>

0 commit comments

Comments
 (0)