Skip to content

Commit

Permalink
Don’t link directly to the media server for downloads.
Browse files Browse the repository at this point in the history
  • Loading branch information
ericholscher committed Aug 29, 2014
1 parent 3384e25 commit 7f1b365
Show file tree
Hide file tree
Showing 11 changed files with 99 additions and 141 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ readthedocs/rtd_tests/builds
readthedocs/rtd_tests/tests/builds
user_builds
user_uploads
prod_artifacts
wheelhouse
whoosh_index
xml_output
1 change: 0 additions & 1 deletion readthedocs/api/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ def get_object_list(self, request):

def dehydrate(self, bundle):
bundle.data['subdomain'] = "http://%s/" % bundle.obj.subdomain
bundle.data['downloads'] = bundle.obj.get_downloads()
return bundle

def post_list(self, request, **kwargs):
Expand Down
17 changes: 6 additions & 11 deletions readthedocs/builds/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,23 +175,18 @@ def get_downloads(self, pretty=False):
data = {}
if pretty:
if project.has_pdf(self.slug):
data['PDF'] = project.get_pdf_url(self.slug)
data['PDF'] = project.get_production_media_url('pdf', self.slug)
if project.has_htmlzip(self.slug):
data['HTML'] = project.get_htmlzip_url(self.slug)
data['HTML'] = project.get_production_media_url('htmlzip', self.slug)
if project.has_epub(self.slug):
data['Epub'] = project.get_epub_url(self.slug)
data['Epub'] = project.get_production_media_url('epub', self.slug)
else:
if project.has_pdf(self.slug):
data['pdf_url'] = project.get_pdf_url(self.slug)
data['pdf_url'] = project.get_production_media_url('pdf', self.slug)
if project.has_htmlzip(self.slug):
data['htmlzip_url'] = project.get_htmlzip_url(self.slug)
data['htmlzip_url'] = project.get_production_media_url('htmlzip', self.slug)
if project.has_epub(self.slug):
data['epub_url'] = project.get_epub_url(self.slug)
#if project.has_manpage(self.slug):
#data['manpage_url'] = project.get_manpage_url(self.slug)
if project.has_dash(self.slug):
data['dash_url'] = project.get_dash_url(self.slug)
data['dash_feed_url'] = project.get_dash_feed_url(self.slug)
data['epub_url'] = project.get_production_media_url('epub', self.slug)
return data

def get_conf_py_path(self):
Expand Down
116 changes: 27 additions & 89 deletions readthedocs/projects/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,90 +371,34 @@ def get_builds_url(self):
'project_slug': self.slug,
})

def get_pdf_url(self, version_slug='latest'):
path = os.path.join(settings.MEDIA_URL, 'pdf', self.slug, version_slug,
'%s.pdf' % self.slug)
return path

def get_pdf_path(self, version_slug='latest'):
path = os.path.join(settings.MEDIA_ROOT, 'pdf', self.slug,
version_slug, '%s.pdf' % self.slug)
return path

def get_epub_url(self, version_slug='latest'):
path = os.path.join(settings.MEDIA_URL, 'epub', self.slug,
version_slug, '%s.epub' % self.slug)
return path

def get_epub_path(self, version_slug='latest'):
path = os.path.join(settings.MEDIA_ROOT, 'epub', self.slug,
version_slug, '%s.epub' % self.slug)
return path

def get_manpage_url(self, version_slug='latest'):
path = os.path.join(settings.MEDIA_URL, 'man', self.slug, version_slug,
'%s.1' % self.slug)
return path

def get_manpage_path(self, version_slug='latest'):
path = os.path.join(settings.MEDIA_ROOT, 'man', self.slug,
version_slug, '%s.1' % self.slug)
return path

def get_htmlzip_url(self, version_slug='latest'):
path = os.path.join(settings.MEDIA_URL, 'htmlzip', self.slug,
version_slug, '%s.zip' % self.slug)
return path

def get_htmlzip_path(self, version_slug='latest'):
path = os.path.join(settings.MEDIA_ROOT, 'htmlzip', self.slug,
version_slug, '%s.zip' % self.slug)
return path

def get_dash_url(self, version_slug='latest'):
path = os.path.join(settings.MEDIA_URL, 'dash', self.slug,
version_slug, '%s.tgz' % self.doc_name)
return path

def get_dash_path(self, version_slug='latest'):
path = os.path.join(settings.MEDIA_ROOT, 'dash', self.slug,
version_slug, '%s.tgz' % self.doc_name)
return path

def get_dash_feed_path(self, version_slug='latest'):
path = os.path.join(settings.MEDIA_ROOT, 'dash', self.slug,
version_slug, '%s.xml' % self.doc_name)
return path

def get_dash_feed_url(self, version_slug='latest'):
path = os.path.join(settings.MEDIA_URL,
'dash',
self.slug,
version_slug,
'%s.xml' % self.doc_name)
def get_production_media_path(self, type, version_slug, include_file=True):
"""
Get file path for media files in production.
This is used to see if these files exist so we can offer them for download.
"""
if DEFAULT_PRIVACY_LEVEL == 'public':
path = os.path.join(settings.MEDIA_ROOT, 'pdf', self.slug, version_slug)
else:
path = os.path.join(settings.PRODUCTION_MEDIA_ARTIFACTS, type, self.slug, version_slug)
if include_file:
path = os.path.join(path, '%s.%s' % (self.slug, type))
return path

def get_json_path(self, version_slug='latest'):
path = os.path.join(settings.MEDIA_ROOT,
'json',
self.slug,
version_slug,
)
def get_production_media_url(self, type, version_slug):
"""
Get the URL for downloading a specific media file.
"""
if DEFAULT_PRIVACY_LEVEL == 'public':
path = os.path.join(settings.MEDIA_URL, type, self.slug, version_slug,
'%s.%s' % (self.slug, type))
else:
path = reverse('project_download_media', kwargs={
'project_slug': self.slug,
'type': type,
'version_slug': version_slug,
})
return path

def get_downloads(self, version_slug='latest'):
downloads = {}
downloads['htmlzip'] = self.get_htmlzip_url()
downloads['epub'] = self.get_epub_url()
downloads['pdf'] = self.get_pdf_url()
downloads['manpage'] = self.get_manpage_url()
downloads['dash'] = self.get_dash_url()
return downloads

@property
def doc_name(self):
return self.slug.replace('_', '-')

@property
def canonical_domain(self):
if not self.clean_canonical_url:
Expand Down Expand Up @@ -646,19 +590,13 @@ def has_aliases(self):
return self.aliases.exists()

def has_pdf(self, version_slug='latest'):
return os.path.exists(self.get_pdf_path(version_slug))

def has_manpage(self, version_slug='latest'):
return os.path.exists(self.get_manpage_path(version_slug))
return os.path.exists(self.get_production_media_path(type='pdf', version_slug=version_slug))

def has_epub(self, version_slug='latest'):
return os.path.exists(self.get_epub_path(version_slug))

def has_dash(self, version_slug='latest'):
return os.path.exists(self.get_dash_path(version_slug))
return os.path.exists(self.get_production_media_path(type='epub', version_slug=version_slug))

def has_htmlzip(self, version_slug='latest'):
return os.path.exists(self.get_htmlzip_path(version_slug))
return os.path.exists(self.get_production_media_path(type='htmlzip', version_slug=version_slug))

@property
def sponsored(self):
Expand Down
12 changes: 4 additions & 8 deletions readthedocs/projects/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,30 +141,26 @@ def move_files(version, results):
if 'localmedia' in results and results['localmedia'][0] == 0:
from_path = version.project.artifact_path(
version=version.slug, type='sphinx_localmedia')
to_path = os.path.join(
settings.MEDIA_ROOT, 'htmlzip', version.project.slug, version.slug)
to_path = version.project.get_production_media_path(type='htmlzip', version_slug=version.slug, include_file=False)
core_utils.copy(from_path, to_path)
if 'search' in results and results['search'][0] == 0:
from_path = version.project.artifact_path(
version=version.slug, type='sphinx_search')
to_path = os.path.join(
settings.MEDIA_ROOT, 'json', version.project.slug, version.slug)
to_path = version.project.get_production_media_path(type='json', version_slug=version.slug, include_file=False)
core_utils.copy(from_path, to_path)
# Always move PDF's because the return code lies.
if 'pdf' in results:
try:
from_path = version.project.artifact_path(
version=version.slug, type='sphinx_pdf')
to_path = os.path.join(
settings.MEDIA_ROOT, 'pdf', version.project.slug, version.slug)
to_path = version.project.get_production_media_path(type='pdf', version_slug=version.slug, include_file=False)
core_utils.copy(from_path, to_path)
except:
pass
if 'epub' in results and results['epub'][0] == 0:
from_path = version.project.artifact_path(
version=version.slug, type='sphinx_epub')
to_path = os.path.join(
settings.MEDIA_ROOT, 'epub', version.project.slug, version.slug)
to_path = version.project.get_production_media_path(type='epub', version_slug=version.slug, include_file=False)
core_utils.copy(from_path, to_path)


Expand Down
4 changes: 4 additions & 0 deletions readthedocs/projects/urls/public.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@
'projects.views.public.project_downloads',
name='project_downloads'),

url(r'^(?P<project_slug>[-\w]+)/download/(?P<type>[-\w]+)/(?P<version_slug>[-\w.]+)/$',
'projects.views.public.project_download_media',
name='project_download_media'),

url(r'^(?P<project_slug>[-\w]+)/badge/$',
'projects.views.public.project_badge',
name='project_badge'),
Expand Down
30 changes: 30 additions & 0 deletions readthedocs/projects/views/public.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import os
import json
import logging
import mimetypes
import md5

from django.core.urlresolvers import reverse
Expand All @@ -10,6 +12,7 @@
from django.template import RequestContext
from django.views.generic import ListView
from django.utils.datastructures import SortedDict
from django.views.static import serve

from taggit.models import Tag
import requests
Expand Down Expand Up @@ -153,6 +156,33 @@ def project_downloads(request, project_slug):
)


def project_download_media(request, project_slug, type, version_slug):
"""
Download a specific piece of media.
Perform an auth check if serving in private mode.
"""
DEFAULT_PRIVACY_LEVEL = getattr(settings, 'DEFAULT_PRIVACY_LEVEL', 'public')
if DEFAULT_PRIVACY_LEVEL == 'public' or settings.DEBUG:
path = os.path.join(settings.MEDIA_URL, type, project_slug, version_slug,
'%s.%s' % (project_slug, type))
return HttpResponseRedirect(path)
else:
# Do private project auth checks
queryset = Project.objects.private(request.user).filter(slug=project_slug)
if queryset.exists():
path = os.path.join("/prod_artifacts/", type, project_slug, version_slug, '%s.%s' % (project_slug, type))
mimetype, encoding = mimetypes.guess_type(path)
mimetype = mimetype or 'application/octet-stream'
response = HttpResponse(mimetype=mimetype)
if encoding:
response["Content-Encoding"] = encoding
response['X-Accel-Redirect'] = path
return response
else:
raise Http404



def search_autocomplete(request):
"""
return a json list of project names
Expand Down
21 changes: 21 additions & 0 deletions readthedocs/rtd_tests/tests/test_privacy.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ def test_private_repo(self):
r = self.client.get('/projects/django-kong/downloads/')
self.assertEqual(r.status_code, 200)

r = self.client.get('/projects/django-kong/download/pdf/latest/')
self.assertEqual(r.status_code, 200)

self.client.login(username='tester', password='test')
r = self.client.get('/')
self.assertTrue('Django Kong' not in r.content)
Expand Down Expand Up @@ -280,3 +283,21 @@ def test_private_doc_serving(self):
self.client.login(username='tester', password='test')
r = self.client.get('/docs/django-kong/en/test-slug/')
self.assertEqual(r.status_code, 401)

def test_private_repo_downloading(self):
"""Check that private projects don't show up in: builds, downloads,
detail, homepage
"""
self._create_kong('private', 'private')

self.client.login(username='eric', password='test')
r = self.client.get('/projects/django-kong/')
self.assertEqual(r.status_code, 200)
r = self.client.get('/builds/django-kong/')
self.assertEqual(r.status_code, 200)
r = self.client.get('/projects/django-kong/downloads/')
self.assertEqual(r.status_code, 200)
r = self.client.get('/projects/django-kong/download/pdf/latest/')
self.assertEqual(r.status_code, 200)
self.assertEqual(r._headers['x-accel-redirect'][1], '/prod_artifacts/pdf/django-kong/latest/django-kong.pdf')
2 changes: 1 addition & 1 deletion readthedocs/search/parse_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def process_all_json_files(version, build_dir=True):
if build_dir:
full_path = version.project.full_json_path(version.slug)
else:
full_path = version.project.get_json_path(version.slug)
full_path = version.project.get_production_media_path(type='json', version_slug=version.slug, include_file=False)
html_files = []
for root, dirs, files in os.walk(full_path):
for filename in fnmatch.filter(files, '*.fjson'):
Expand Down
4 changes: 4 additions & 0 deletions readthedocs/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
CNAME_ROOT = os.path.join(SITE_ROOT, 'cnames')
LOGS_ROOT = os.path.join(SITE_ROOT, 'logs')

# A new base for production files
PRODUCTION_ROOT = os.path.join(SITE_ROOT, 'prod_artifacts')
PRODUCTION_MEDIA_ARTIFACTS = os.path.join(PRODUCTION_ROOT, 'media')

MEDIA_ROOT = '%s/media/' % (SITE_ROOT)
MEDIA_URL = '/media/'
ADMIN_MEDIA_PREFIX = '/media/admin/'
Expand Down
32 changes: 1 addition & 31 deletions readthedocs/templates/core/project_downloads.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{% load i18n %}

{% for version, dict in version_data.items %}
{% if dict.pdf_url %}
<li class="module-item col-span">
Expand Down Expand Up @@ -30,37 +31,6 @@
</li>
{% endif %}

{% comment %}
{% if dict.manpage_url %}
<li class="module-item col-span">
<a class="module-item-title" href="{{ dict.manpage_url }}">
{% blocktrans %}
{{ version }} Manpage
{% endblocktrans %}
</a>
</li>
{% endif %}
{% endcomment %}

{% if dict.dash_url %}
<li class="module-item col-span">
<a class="module-item-title" href="{{ dict.dash_url }}">
{% blocktrans %}
{{ version }} Dash docset
{% endblocktrans %}
</a>
</li>
{% endif %}

{% if dict.dash_feed_url %}
<li class="module-item col-span">
<a class="module-item-title" href="dash-feed://{% filter urlencode %}{{ media_url_prefix }}{{ dict.dash_feed_url }}{% endfilter %}">
{% blocktrans %}
{{ version }} Dash feed
{% endblocktrans %}
</a>
</li>
{% endif %}
{% empty %}
<li class="module-item col-span">
{% trans "No downloads for this project." %}
Expand Down

0 comments on commit 7f1b365

Please sign in to comment.