From 54e695331b07a572e0f37085725d23df69980628 Mon Sep 17 00:00:00 2001
From: Jon Dufresne <jon.dufresne@gmail.com>
Date: Wed, 15 Oct 2014 18:03:40 -0700
Subject: [PATCH] Fixed #20221 -- Allowed some functions that use mark_safe()
 to result in SafeText.

Thanks Baptiste Mispelon for the report.
---
 django/utils/html.py                 |  6 +++---
 django/utils/text.py                 |  5 +++--
 tests/utils_tests/test_safestring.py | 15 +++++++++++++++
 3 files changed, 21 insertions(+), 5 deletions(-)

diff --git a/django/utils/html.py b/django/utils/html.py
index 43f2141d6b..115ccbbaaa 100644
--- a/django/utils/html.py
+++ b/django/utils/html.py
@@ -10,7 +10,7 @@ from django.utils.deprecation import RemovedInDjango20Warning
 from django.utils.encoding import force_text, force_str
 from django.utils.functional import allow_lazy
 from django.utils.http import RFC3986_GENDELIMS, RFC3986_SUBDELIMS
-from django.utils.safestring import SafeData, mark_safe
+from django.utils.safestring import SafeData, SafeText, mark_safe
 from django.utils import six
 from django.utils.six.moves.urllib.parse import parse_qsl, quote, unquote, urlencode, urlsplit, urlunsplit
 from django.utils.text import normalize_newlines
@@ -47,7 +47,7 @@ def escape(text):
     """
     return mark_safe(force_text(text).replace('&', '&amp;').replace('<', '&lt;')
         .replace('>', '&gt;').replace('"', '&quot;').replace("'", '&#39;'))
-escape = allow_lazy(escape, six.text_type)
+escape = allow_lazy(escape, six.text_type, SafeText)
 
 _js_escapes = {
     ord('\\'): '\\u005C',
@@ -70,7 +70,7 @@ _js_escapes.update((ord('%c' % z), '\\u%04X' % z) for z in range(32))
 def escapejs(value):
     """Hex encodes characters for use in JavaScript strings."""
     return mark_safe(force_text(value).translate(_js_escapes))
-escapejs = allow_lazy(escapejs, six.text_type)
+escapejs = allow_lazy(escapejs, six.text_type, SafeText)
 
 
 def conditional_escape(text):
diff --git a/django/utils/text.py b/django/utils/text.py
index 68eb713026..b2645e8697 100644
--- a/django/utils/text.py
+++ b/django/utils/text.py
@@ -12,7 +12,7 @@ from django.utils.functional import allow_lazy, SimpleLazyObject
 from django.utils import six
 from django.utils.six.moves import html_entities
 from django.utils.translation import ugettext_lazy, ugettext as _, pgettext
-from django.utils.safestring import mark_safe
+from django.utils.safestring import SafeText, mark_safe
 
 if six.PY2:
     # Import force_unicode even though this module doesn't use it, because some
@@ -442,10 +442,11 @@ def slugify(value):
     underscores) and converts spaces to hyphens. Also strips leading and
     trailing whitespace.
     """
+    value = force_text(value)
     value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode('ascii')
     value = re.sub('[^\w\s-]', '', value).strip().lower()
     return mark_safe(re.sub('[-\s]+', '-', value))
-slugify = allow_lazy(slugify, six.text_type)
+slugify = allow_lazy(slugify, six.text_type, SafeText)
 
 
 def camel_case_to_spaces(value):
diff --git a/tests/utils_tests/test_safestring.py b/tests/utils_tests/test_safestring.py
index 65242e46fb..c953412430 100644
--- a/tests/utils_tests/test_safestring.py
+++ b/tests/utils_tests/test_safestring.py
@@ -6,6 +6,8 @@ from django.utils.encoding import force_text, force_bytes
 from django.utils.functional import lazy
 from django.utils.safestring import mark_safe, mark_for_escaping, SafeData, EscapeData
 from django.utils import six
+from django.utils import text
+from django.utils import html
 
 lazystr = lazy(force_text, six.text_type)
 lazybytes = lazy(force_bytes, bytes)
@@ -47,3 +49,16 @@ class SafeStringTest(TestCase):
     def test_html(self):
         s = '<h1>interop</h1>'
         self.assertEqual(s, mark_safe(s).__html__())
+
+    def test_add_lazy_safe_text_and_safe_text(self):
+        s = html.escape(lazystr('a'))
+        s += mark_safe('&b')
+        self.assertRenderEqual('{{ s }}', 'a&b', s=s)
+
+        s = html.escapejs(lazystr('a'))
+        s += mark_safe('&b')
+        self.assertRenderEqual('{{ s }}', 'a&b', s=s)
+
+        s = text.slugify(lazystr('a'))
+        s += mark_safe('&b')
+        self.assertRenderEqual('{{ s }}', 'a&b', s=s)