diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py
index 0fb74ca00..3c4be8aeb 100644
--- a/rest_framework/renderers.py
+++ b/rest_framework/renderers.py
@@ -418,7 +418,7 @@ class BrowsableAPIRenderer(BaseRenderer):
if render_style == 'binary':
return '[%d bytes of binary content]' % len(content)
- return content
+ return content.decode('utf-8') if isinstance(content, bytes) else content
def show_form_for_method(self, view, method, request, obj):
"""
diff --git a/rest_framework/templates/rest_framework/base.html b/rest_framework/templates/rest_framework/base.html
index 9207f049b..a88e1591c 100644
--- a/rest_framework/templates/rest_framework/base.html
+++ b/rest_framework/templates/rest_framework/base.html
@@ -77,7 +77,7 @@
{% block request_forms %}
-
+
{% if 'GET' in allowed_methods %}
diff --git a/rest_framework/templatetags/rest_framework.py b/rest_framework/templatetags/rest_framework.py
index 79dd953ff..7bfa8f599 100644
--- a/rest_framework/templatetags/rest_framework.py
+++ b/rest_framework/templatetags/rest_framework.py
@@ -4,9 +4,9 @@ from collections import OrderedDict
from django import template
from django.template import loader
from django.urls import NoReverseMatch, reverse
-from django.utils.encoding import force_str, iri_to_uri
+from django.utils.encoding import iri_to_uri
from django.utils.html import escape, format_html, smart_urlquote
-from django.utils.safestring import SafeData, mark_safe
+from django.utils.safestring import mark_safe
from rest_framework.compat import apply_markdown, pygments_highlight
from rest_framework.renderers import HTMLFormRenderer
@@ -311,85 +311,6 @@ def smart_urlquote_wrapper(matched_url):
return None
-@register.filter(needs_autoescape=True)
-def urlize_quoted_links(text, trim_url_limit=None, nofollow=True, autoescape=True):
- """
- Converts any URLs in text into clickable links.
-
- Works on http://, https://, www. links, and also on links ending in one of
- the original seven gTLDs (.com, .edu, .gov, .int, .mil, .net, and .org).
- Links can have trailing punctuation (periods, commas, close-parens) and
- leading punctuation (opening parens) and it'll still do the right thing.
-
- If trim_url_limit is not None, the URLs in link text longer than this limit
- will truncated to trim_url_limit-3 characters and appended with an ellipsis.
-
- If nofollow is True, the URLs in link text will get a rel="nofollow"
- attribute.
-
- If autoescape is True, the link text and URLs will get autoescaped.
- """
- def trim_url(x, limit=trim_url_limit):
- return limit is not None and (len(x) > limit and ('%s...' % x[:max(0, limit - 3)])) or x
-
- safe_input = isinstance(text, SafeData)
-
- # Unfortunately, Django built-in cannot be used here, because escaping
- # is to be performed on words, which have been forcibly coerced to text
- def conditional_escape(text):
- return escape(text) if autoescape and not safe_input else text
-
- words = word_split_re.split(force_str(text))
- for i, word in enumerate(words):
- if '.' in word or '@' in word or ':' in word:
- # Deal with punctuation.
- lead, middle, trail = '', word, ''
- for punctuation in TRAILING_PUNCTUATION:
- if middle.endswith(punctuation):
- middle = middle[:-len(punctuation)]
- trail = punctuation + trail
- for opening, closing in WRAPPING_PUNCTUATION:
- if middle.startswith(opening):
- middle = middle[len(opening):]
- lead = lead + opening
- # Keep parentheses at the end only if they're balanced.
- if (
- middle.endswith(closing) and
- middle.count(closing) == middle.count(opening) + 1
- ):
- middle = middle[:-len(closing)]
- trail = closing + trail
-
- # Make URL we want to point to.
- url = None
- nofollow_attr = ' rel="nofollow"' if nofollow else ''
- if simple_url_re.match(middle):
- url = smart_urlquote_wrapper(middle)
- elif simple_url_2_re.match(middle):
- url = smart_urlquote_wrapper('http://%s' % middle)
- elif ':' not in middle and simple_email_re.match(middle):
- local, domain = middle.rsplit('@', 1)
- try:
- domain = domain.encode('idna').decode('ascii')
- except UnicodeError:
- continue
- url = 'mailto:%s@%s' % (local, domain)
- nofollow_attr = ''
-
- # Make link.
- if url:
- trimmed = trim_url(middle)
- lead, trail = conditional_escape(lead), conditional_escape(trail)
- url, trimmed = conditional_escape(url), conditional_escape(trimmed)
- middle = '%s' % (url, nofollow_attr, trimmed)
- words[i] = '%s%s%s' % (lead, middle, trail)
- else:
- words[i] = conditional_escape(word)
- else:
- words[i] = conditional_escape(word)
- return mark_safe(''.join(words))
-
-
@register.filter
def break_long_headers(header):
"""
diff --git a/tests/test_templatetags.py b/tests/test_templatetags.py
index 28d6b4011..cdf10ab1e 100644
--- a/tests/test_templatetags.py
+++ b/tests/test_templatetags.py
@@ -2,13 +2,14 @@ import unittest
from django.template import Context, Template
from django.test import TestCase
+from django.utils.html import urlize
from rest_framework.compat import coreapi, coreschema
from rest_framework.relations import Hyperlink
from rest_framework.templatetags import rest_framework
from rest_framework.templatetags.rest_framework import (
add_nested_class, add_query_param, as_string, break_long_headers,
- format_value, get_pagination_html, schema_links, urlize_quoted_links
+ format_value, get_pagination_html, schema_links
)
from rest_framework.test import APIRequestFactory
@@ -246,7 +247,7 @@ class Issue1386Tests(TestCase):
def test_issue_1386(self):
"""
- Test function urlize_quoted_links with different args
+ Test function urlize with different args
"""
correct_urls = [
"asdf.com",
@@ -255,7 +256,7 @@ class Issue1386Tests(TestCase):
"as.d8f.ghj8.gov",
]
for i in correct_urls:
- res = urlize_quoted_links(i)
+ res = urlize(i)
self.assertNotEqual(res, i)
self.assertIn(i, res)
@@ -264,11 +265,11 @@ class Issue1386Tests(TestCase):
"asdf.netnet",
]
for i in incorrect_urls:
- res = urlize_quoted_links(i)
+ res = urlize(i)
self.assertEqual(i, res)
# example from issue #1386, this shouldn't raise an exception
- urlize_quoted_links("asdf:[/p]zxcv.com")
+ urlize("asdf:[/p]zxcv.com")
def test_smart_urlquote_wrapper_handles_value_error(self):
def mock_smart_urlquote(url):
@@ -289,7 +290,10 @@ class URLizerTests(TestCase):
For all items in dict test assert that the value is urlized key
"""
for original, urlized in data.items():
- assert urlize_quoted_links(original, nofollow=False) == urlized
+ print('====')
+ print(repr(urlize(original, nofollow=False)))
+ print(repr(urlized))
+ assert urlize(original, nofollow=False) == urlized
def test_json_with_url(self):
"""
@@ -297,26 +301,26 @@ class URLizerTests(TestCase):
"""
data = {}
data['"url": "http://api/users/1/", '] = \
- '"url": "http://api/users/1/", '
+ '"url": "http://api/users/1/", '
data['"foo_set": [\n "http://api/foos/1/"\n], '] = \
- '"foo_set": [\n "http://api/foos/1/"\n], '
+ '"foo_set": [\n "http://api/foos/1/"\n], '
self._urlize_dict_check(data)
def test_template_render_with_autoescape(self):
"""
Test that HTML is correctly escaped in Browsable API views.
"""
- template = Template("{% load rest_framework %}{{ content|urlize_quoted_links }}")
+ template = Template("{% load rest_framework %}{{ content|urlize }}")
rendered = template.render(Context({'content': ' http://example.com'}))
assert rendered == '<script>alert()</script>' \
' http://example.com'
def test_template_render_with_noautoescape(self):
"""
- Test if the autoescape value is getting passed to urlize_quoted_links filter.
+ Test if the autoescape value is getting passed to urlize filter.
"""
template = Template("{% load rest_framework %}"
- "{% autoescape off %}{{ content|urlize_quoted_links }}"
+ "{% autoescape off %}{{ content|urlize }}"
"{% endautoescape %}")
rendered = template.render(Context({'content': ' "http://example.com" '}))
assert rendered == ' "http://example.com" '