From dcdbd7ea3694e0b6e1bb69243f9f164bbd9f9e32 Mon Sep 17 00:00:00 2001 From: Matt Westcott Date: Tue, 28 Apr 2015 13:02:30 +0100 Subject: [PATCH] Fall back on ASCII filename if setting Content-Disposition fails - fixes #1166 --- wagtail/wagtaildocs/tests.py | 16 ++++++++++++++++ wagtail/wagtaildocs/views/serve.py | 14 ++++++++++---- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/wagtail/wagtaildocs/tests.py b/wagtail/wagtaildocs/tests.py index a7943e4926..c80facf630 100644 --- a/wagtail/wagtaildocs/tests.py +++ b/wagtail/wagtaildocs/tests.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from six import b import unittest import mock @@ -580,6 +582,20 @@ class TestServeView(TestCase): self.assertEqual(response.status_code, 404) +class TestServeWithUnicodeFilename(TestCase): + def setUp(self): + self.document = models.Document(title="Test document") + + # Setting this filename in the content-disposition header fails on Django <1.8, Python 2 + # due to https://code.djangoproject.com/ticket/20889 + self.filename = 'docs\u0627\u0644\u0643\u0627\u062a\u062f\u0631\u0627\u064a\u064a\u0629_\u0648\u0627\u0644\u0633\u0648\u0642' + self.document.file.save(self.filename, ContentFile("A boring example document")) + + def test_response_code(self): + response = self.client.get(reverse('wagtaildocs_serve', args=(self.document.id, self.filename))) + self.assertEqual(response.status_code, 200) + + class TestDocumentRichTextLinkHandler(TestCase): fixtures = ['test.json'] diff --git a/wagtail/wagtaildocs/views/serve.py b/wagtail/wagtaildocs/views/serve.py index f6382ca93f..4bd8d45569 100644 --- a/wagtail/wagtaildocs/views/serve.py +++ b/wagtail/wagtaildocs/views/serve.py @@ -1,6 +1,8 @@ from django.shortcuts import get_object_or_404 from wsgiref.util import FileWrapper -from django.http import StreamingHttpResponse +from django.http import StreamingHttpResponse, BadHeaderError + +from unidecode import unidecode from wagtail.wagtaildocs.models import Document, document_served @@ -10,9 +12,13 @@ def serve(request, document_id, document_filename): wrapper = FileWrapper(doc.file) response = StreamingHttpResponse(wrapper, content_type='application/octet-stream') - # TODO: strip out weird characters like semicolons from the filename - # (there doesn't seem to be an official way of escaping them) - response['Content-Disposition'] = 'attachment; filename=%s' % doc.filename + try: + response['Content-Disposition'] = 'attachment; filename=%s' % doc.filename + except BadHeaderError: + # Unicode filenames can fail on Django <1.8, Python 2 due to + # https://code.djangoproject.com/ticket/20889 - try with an ASCIIfied version of the name + response['Content-Disposition'] = 'attachment; filename=%s' % unidecode(doc.filename) + response['Content-Length'] = doc.file.size # Send document_served signal