mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Fixed #34194 -- Added django.utils.http.content_disposition_header().
This commit is contained in:
		
				
					committed by
					
						 Mariusz Felisiak
						Mariusz Felisiak
					
				
			
			
				
	
			
			
			
						parent
						
							3d3e955efa
						
					
				
				
					commit
					cbce427c17
				
			| @@ -8,7 +8,7 @@ import sys | ||||
| import time | ||||
| from email.header import Header | ||||
| from http.client import responses | ||||
| from urllib.parse import quote, urlparse | ||||
| from urllib.parse import urlparse | ||||
|  | ||||
| from django.conf import settings | ||||
| from django.core import signals, signing | ||||
| @@ -18,7 +18,7 @@ from django.http.cookie import SimpleCookie | ||||
| from django.utils import timezone | ||||
| from django.utils.datastructures import CaseInsensitiveMapping | ||||
| from django.utils.encoding import iri_to_uri | ||||
| from django.utils.http import http_date | ||||
| from django.utils.http import content_disposition_header, http_date | ||||
| from django.utils.regex_helper import _lazy_re_compile | ||||
|  | ||||
| _charset_from_content_type_re = _lazy_re_compile( | ||||
| @@ -569,20 +569,10 @@ class FileResponse(StreamingHttpResponse): | ||||
|             else: | ||||
|                 self.headers["Content-Type"] = "application/octet-stream" | ||||
|  | ||||
|         if filename: | ||||
|             disposition = "attachment" if self.as_attachment else "inline" | ||||
|             try: | ||||
|                 filename.encode("ascii") | ||||
|                 file_expr = 'filename="{}"'.format( | ||||
|                     filename.replace("\\", "\\\\").replace('"', r"\"") | ||||
|                 ) | ||||
|             except UnicodeEncodeError: | ||||
|                 file_expr = "filename*=utf-8''{}".format(quote(filename)) | ||||
|             self.headers["Content-Disposition"] = "{}; {}".format( | ||||
|                 disposition, file_expr | ||||
|             ) | ||||
|         elif self.as_attachment: | ||||
|             self.headers["Content-Disposition"] = "attachment" | ||||
|         if content_disposition := content_disposition_header( | ||||
|             self.as_attachment, filename | ||||
|         ): | ||||
|             self.headers["Content-Disposition"] = content_disposition | ||||
|  | ||||
|  | ||||
| class HttpResponseRedirectBase(HttpResponse): | ||||
|   | ||||
| @@ -10,6 +10,7 @@ from urllib.parse import ( | ||||
|     _coerce_args, | ||||
|     _splitnetloc, | ||||
|     _splitparams, | ||||
|     quote, | ||||
|     scheme_chars, | ||||
|     unquote, | ||||
| ) | ||||
| @@ -425,3 +426,24 @@ def parse_header_parameters(line): | ||||
|                 value = unquote(value, encoding=encoding) | ||||
|             pdict[name] = value | ||||
|     return key, pdict | ||||
|  | ||||
|  | ||||
| def content_disposition_header(as_attachment, filename): | ||||
|     """ | ||||
|     Construct a Content-Disposition HTTP header value from the given filename | ||||
|     as specified by RFC 6266. | ||||
|     """ | ||||
|     if filename: | ||||
|         disposition = "attachment" if as_attachment else "inline" | ||||
|         try: | ||||
|             filename.encode("ascii") | ||||
|             file_expr = 'filename="{}"'.format( | ||||
|                 filename.replace("\\", "\\\\").replace('"', r"\"") | ||||
|             ) | ||||
|         except UnicodeEncodeError: | ||||
|             file_expr = "filename*=utf-8''{}".format(quote(filename)) | ||||
|         return f"{disposition}; {file_expr}" | ||||
|     elif as_attachment: | ||||
|         return "attachment" | ||||
|     else: | ||||
|         return None | ||||
|   | ||||
| @@ -729,6 +729,15 @@ escaping HTML. | ||||
|  | ||||
|     Outputs a string in the format ``Wdy, DD Mon YYYY HH:MM:SS GMT``. | ||||
|  | ||||
| .. function:: content_disposition_header(as_attachment, filename) | ||||
|  | ||||
|     .. versionadded:: 4.2 | ||||
|  | ||||
|     Constructs a ``Content-Disposition`` HTTP header value from the given | ||||
|     ``filename`` as specified by :rfc:`6266`. Returns ``None`` if | ||||
|     ``as_attachment`` is ``False`` and ``filename`` is ``None``, otherwise | ||||
|     returns a string suitable for the ``Content-Disposition`` HTTP header. | ||||
|  | ||||
| .. function:: base36_to_int(s) | ||||
|  | ||||
|     Converts a base 36 string to an integer. | ||||
|   | ||||
| @@ -321,6 +321,9 @@ Utilities | ||||
|   documented functions for handling URL redirects. The Django functions were | ||||
|   not affected. | ||||
|  | ||||
| * The new :func:`django.utils.http.content_disposition_header` function returns | ||||
|   a ``Content-Disposition`` HTTP header value as specified by :rfc:`6266`. | ||||
|  | ||||
| Validators | ||||
| ~~~~~~~~~~ | ||||
|  | ||||
|   | ||||
| @@ -7,6 +7,7 @@ from django.test import SimpleTestCase | ||||
| from django.utils.datastructures import MultiValueDict | ||||
| from django.utils.http import ( | ||||
|     base36_to_int, | ||||
|     content_disposition_header, | ||||
|     escape_leading_slashes, | ||||
|     http_date, | ||||
|     int_to_base36, | ||||
| @@ -511,3 +512,28 @@ class ParseHeaderParameterTests(unittest.TestCase): | ||||
|         for raw_line, expected_title in test_data: | ||||
|             parsed = parse_header_parameters(raw_line) | ||||
|             self.assertEqual(parsed[1]["title"], expected_title) | ||||
|  | ||||
|  | ||||
| class ContentDispositionHeaderTests(unittest.TestCase): | ||||
|     def test_basic(self): | ||||
|         tests = ( | ||||
|             ((False, None), None), | ||||
|             ((False, "example"), 'inline; filename="example"'), | ||||
|             ((True, None), "attachment"), | ||||
|             ((True, "example"), 'attachment; filename="example"'), | ||||
|             ( | ||||
|                 (True, '"example" file\\name'), | ||||
|                 'attachment; filename="\\"example\\" file\\\\name"', | ||||
|             ), | ||||
|             ((True, "espécimen"), "attachment; filename*=utf-8''esp%C3%A9cimen"), | ||||
|             ( | ||||
|                 (True, '"espécimen" filename'), | ||||
|                 "attachment; filename*=utf-8''%22esp%C3%A9cimen%22%20filename", | ||||
|             ), | ||||
|         ) | ||||
|  | ||||
|         for (is_attachment, filename), expected in tests: | ||||
|             with self.subTest(is_attachment=is_attachment, filename=filename): | ||||
|                 self.assertEqual( | ||||
|                     content_disposition_header(is_attachment, filename), expected | ||||
|                 ) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user