diff --git a/perma_web/fabfile/dev.py b/perma_web/fabfile/dev.py index 873bfff73..0ad1dd9f3 100644 --- a/perma_web/fabfile/dev.py +++ b/perma_web/fabfile/dev.py @@ -105,7 +105,7 @@ def sauce_tunnel(): Set up Sauce tunnel before running functional tests targeted at localhost. """ if subprocess.call(['which','sc']) == 1: # error return code -- program not found - sys.exit("Please check that the `sc` program is installed and in your path. To install: https://docs.saucelabs.com/reference/sauce-connect/") + sys.exit("Please check that the `sc` program is installed and in your path. To install: https://wiki.saucelabs.com/display/DOCS/Sauce+Connect+Proxy") local("sc -u %s -k %s" % (settings.SAUCE_USERNAME, settings.SAUCE_ACCESS_KEY)) diff --git a/perma_web/perma/templates/archive/download_to_view_pdf.html b/perma_web/perma/templates/archive/download_to_view_pdf.html new file mode 100644 index 000000000..63f31ce82 --- /dev/null +++ b/perma_web/perma/templates/archive/download_to_view_pdf.html @@ -0,0 +1,5 @@ +
+

Perma.cc can't display this file type on mobile, but you can view or download the archived file by clicking below.

+

File type {{mime_type}}

+ View/Download File +
\ No newline at end of file diff --git a/perma_web/perma/templates/archive/single-link.html b/perma_web/perma/templates/archive/single-link.html index 269f71384..77965dcfb 100755 --- a/perma_web/perma/templates/archive/single-link.html +++ b/perma_web/perma/templates/archive/single-link.html @@ -239,6 +239,7 @@ {% endif %} + {% endblock %} {% block mainContent %} @@ -246,7 +247,11 @@ {% if link.user_deleted %} {% include "archive/deleted.html" %} {% elif can_view %} - {% include "archive/iframe.html" %} + {% if redirect_to_download_view %} + {% include "archive/download_to_view_pdf.html" %} + {% else %} + {% include "archive/iframe.html" %} + {% endif %} {% elif link.is_private %} {% include "archive/dark-archive.html" %} {% endif %} diff --git a/perma_web/perma/tests/test_views_common.py b/perma_web/perma/tests/test_views_common.py index 1ea1f66ea..3f1b14a16 100644 --- a/perma_web/perma/tests/test_views_common.py +++ b/perma_web/perma/tests/test_views_common.py @@ -1,7 +1,7 @@ from django.conf import settings from django.core import mail from django.core.urlresolvers import reverse -from django.test import override_settings +from django.test import override_settings, Client from perma.urls import urlpatterns from perma.models import Registrar @@ -54,6 +54,17 @@ def test_dark_archive(self): response = self.get('single_linky', reverse_kwargs={'kwargs': {'guid': 'ABCD-0001'}}) self.assertIn("This record is private.", response.content) + def test_redirect_to_download(self): + # Give user option to download to view pdf if on mobile + client = Client(HTTP_USER_AGENT='Mozilla/5.0 (iPhone; CPU iPhone OS 6_1_4 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10B350 Safari/8536.25') + response = client.get(reverse('single_linky', kwargs={'guid': '7CF8-SS4G'})) + self.assertIn("Perma.cc can\'t display this file type on mobile", response.content) + + # If not on mobile, display link as normal + client = Client(HTTP_USER_AGENT='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/601.7.7 (KHTML, like Gecko) Version/9.1.2 Safari/601.7.7') + response = client.get(reverse('single_linky', kwargs={'guid': '7CF8-SS4G'})) + self.assertNotIn("Perma.cc can\'t display this file type on mobile", response.content) + def test_deleted(self): response = self.get('single_linky', reverse_kwargs={'kwargs': {'guid': 'ABCD-0003'}}) self.assertIn("This record has been deleted.", response.content) diff --git a/perma_web/perma/utils.py b/perma_web/perma/utils.py index 8fa5399c3..3b4c1f7ab 100644 --- a/perma_web/perma/utils.py +++ b/perma_web/perma/utils.py @@ -9,6 +9,7 @@ from netaddr import IPAddress, IPNetwork from functools import wraps import requests +from ua_parser import user_agent_parser from django.core.paginator import Paginator from django.db.models import Q @@ -252,3 +253,15 @@ def get_lat_long(address): warn("Error connecting to geocoding API: %s" % r.status_code) +def parse_user_agent(user_agent_str): + return user_agent_parser.ParseUserAgent(user_agent_str) + + +### pdf handling on mobile ### + +def redirect_to_download(capture_mime_type, user_agent_str): + # redirecting to a page with a download button (and not attempting to display) + # if mobile apple device, and the request is a pdf + parsed_agent = parse_user_agent(user_agent_str) + + return "Mobile" in parsed_agent["family"] and "pdf" in capture_mime_type diff --git a/perma_web/perma/views/common.py b/perma_web/perma/views/common.py index b3ba38277..2a3103b72 100755 --- a/perma_web/perma/views/common.py +++ b/perma_web/perma/views/common.py @@ -6,7 +6,6 @@ from ratelimit.decorators import ratelimit from datetime import timedelta from wsgiref.handlers import format_date_time -from ua_parser import user_agent_parser from urllib import urlencode from django.contrib.auth.views import redirect_to_login @@ -24,7 +23,7 @@ from ..models import Link, Registrar, Organization, LinkUser from ..forms import ContactForm -from ..utils import if_anonymous, ratelimit_ip_key +from ..utils import if_anonymous, ratelimit_ip_key, redirect_to_download, parse_user_agent from ..email import send_admin_email, send_user_email_copy_admins from warcio.warcwriter import BufferWARCWriter @@ -109,6 +108,7 @@ def single_linky(request, guid): """ Given a Perma ID, serve it up. """ + raw_user_agent = request.META.get('HTTP_USER_AGENT', '') # Create a canonical version of guid (non-alphanumerics removed, hyphens every 4 characters, uppercase), # and forward to that if it's different from current guid. @@ -195,7 +195,7 @@ def make_warcinfo(filename, guid, coll_title, coll_desc, rec_title, pages): # safari=1 in the query string indicates that the redirect has already happened. # See http://labs.fundbox.com/third-party-cookies-with-ie-at-2am/ if link.is_private and not request.GET.get('safari'): - user_agent = user_agent_parser.ParseUserAgent(request.META.get('HTTP_USER_AGENT', '')) + user_agent = parse_user_agent(raw_user_agent) if user_agent.get('family') == 'Safari': return redirect_to_login(request.build_absolute_uri(), "//%s%s" % (settings.WARC_HOST, reverse('user_management_set_safari_cookie'))) @@ -210,6 +210,16 @@ def make_warcinfo(filename, guid, coll_title, coll_desc, rec_title, pages): if (not capture or capture.status != 'success') and link.screenshot_capture and link.screenshot_capture.status == 'success': return HttpResponseRedirect(reverse('single_linky', args=[guid])+"?type=image") + try: + capture_mime_type = capture.mime_type() + except AttributeError: + # If capture is deleted, then mime type does not exist. Catch error. + capture_mime_type = None + + # Special handling for mobile pdf viewing because it can be buggy + # Redirecting to a download page if on mobile + redirect_to_download_view = redirect_to_download(capture_mime_type, raw_user_agent) + # If this record was just created by the current user, show them a new record message new_record = request.user.is_authenticated() and link.created_by_id == request.user.id and not link.user_deleted \ and link.creation_timestamp > timezone.now() - timedelta(seconds=300) @@ -224,6 +234,8 @@ def make_warcinfo(filename, guid, coll_title, coll_desc, rec_title, pages): context = { 'link': link, + 'redirect_to_download_view': redirect_to_download_view, + 'mime_type': capture_mime_type, 'can_view': request.user.can_view(link), 'can_edit': request.user.can_edit(link), 'can_delete': request.user.can_delete(link), @@ -239,9 +251,9 @@ def make_warcinfo(filename, guid, coll_title, coll_desc, rec_title, pages): response = render(request, 'archive/single-link.html', context) date_header = format_date_time(mktime(link.creation_timestamp.timetuple())) - link_memento = protocol + settings.HOST + '/' + link.guid + link_memento = protocol + settings.HOST + '/' + link.guid link_timegate = protocol + settings.WARC_HOST + settings.TIMEGATE_WARC_ROUTE + '/' + link.safe_url - link_timemap = protocol + settings.WARC_HOST + settings.WARC_ROUTE + '/timemap/*/' + link.safe_url + link_timemap = protocol + settings.WARC_HOST + settings.WARC_ROUTE + '/timemap/*/' + link.safe_url response['Memento-Datetime'] = date_header link_memento_headers = '<{0}>; rel="original"; datetime="{1}",<{2}>; rel="memento"; datetime="{1}",<{3}>; rel="timegate",<{4}>; rel="timemap"; type="application/link-format"'