From b2bd08bb7a912a1504f5fb5018f5317e6b5423cd Mon Sep 17 00:00:00 2001 From: Sjbrgsn Date: Sat, 21 Dec 2019 11:45:54 +0100 Subject: [PATCH] =?UTF-8?q?Fixed=20#30892=20--=20Fixed=20slugify()=20and?= =?UTF-8?q?=20admin's=20URLify.js=20for=20"=C4=B0".?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thanks Luis Nell for the implementation idea and very detailed report. Co-Authored-By: Mariusz Felisiak --- AUTHORS | 1 + django/contrib/admin/static/admin/js/urlify.js | 3 ++- django/utils/text.py | 2 +- docs/ref/utils.txt | 2 +- tests/admin_views/tests.py | 15 ++++++++------- tests/utils_tests/test_text.py | 1 + 6 files changed, 14 insertions(+), 10 deletions(-) diff --git a/AUTHORS b/AUTHORS index 8b4492cc5c..5b7d67d06c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -185,6 +185,7 @@ answer newbie questions, and generally made Django that much better: Christian Metts Christian Oudard Christian Tanzer + Christoffer Sjöbergsson Christophe Pettus Christopher Adams Christopher Babiak diff --git a/django/contrib/admin/static/admin/js/urlify.js b/django/contrib/admin/static/admin/js/urlify.js index c3342b9d59..8a3842c9bc 100644 --- a/django/contrib/admin/static/admin/js/urlify.js +++ b/django/contrib/admin/static/admin/js/urlify.js @@ -177,6 +177,7 @@ var r = new RegExp('\\b(' + removeList.join('|') + ')\\b', 'gi'); s = s.replace(r, ''); } + s = s.toLowerCase(); // convert to lowercase // if downcode doesn't hit, the char will be stripped here if (allowUnicode) { // Keep Unicode letters including both lowercase and uppercase @@ -189,7 +190,7 @@ s = s.replace(/[-\s]+/g, '-'); // convert spaces to hyphens s = s.substring(0, num_chars); // trim to first num_chars chars s = s.replace(/-+$/g, ''); // trim any trailing hyphens - return s.toLowerCase(); // convert to lowercase + return s; } window.URLify = URLify; })(); diff --git a/django/utils/text.py b/django/utils/text.py index 5e1409116e..110e088ddf 100644 --- a/django/utils/text.py +++ b/django/utils/text.py @@ -402,7 +402,7 @@ def slugify(value, allow_unicode=False): value = unicodedata.normalize('NFKC', value) else: value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode('ascii') - value = re.sub(r'[^\w\s-]', '', value).strip().lower() + value = re.sub(r'[^\w\s-]', '', value.lower()).strip() return re.sub(r'[-\s]+', '-', value) diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt index d8af302c0e..4dafc9cee5 100644 --- a/docs/ref/utils.txt +++ b/docs/ref/utils.txt @@ -830,10 +830,10 @@ appropriate entities. Converts a string to a URL slug by: #. Converting to ASCII if ``allow_unicode`` is ``False`` (the default). + #. Converting to lowercase. #. Removing characters that aren't alphanumerics, underscores, hyphens, or whitespace. #. Removing leading and trailing whitespace. - #. Converting to lowercase. #. Replacing any whitespace or repeated dashes with single dashes. For example:: diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py index 71db79ae2e..4b4b118987 100644 --- a/tests/admin_views/tests.py +++ b/tests/admin_views/tests.py @@ -4442,13 +4442,13 @@ class SeleniumTests(AdminSeleniumTestCase): # Main form ---------------------------------------------------------- self.selenium.find_element_by_id('id_pubdate').send_keys('2012-02-18') self.select_option('#id_status', 'option two') - self.selenium.find_element_by_id('id_name').send_keys(' this is the mAin nÀMë and it\'s awεšomeııı') + self.selenium.find_element_by_id('id_name').send_keys(' this is the mAin nÀMë and it\'s awεšomeıııİ') slug1 = self.selenium.find_element_by_id('id_slug1').get_attribute('value') slug2 = self.selenium.find_element_by_id('id_slug2').get_attribute('value') slug3 = self.selenium.find_element_by_id('id_slug3').get_attribute('value') - self.assertEqual(slug1, 'main-name-and-its-awesomeiii-2012-02-18') - self.assertEqual(slug2, 'option-two-main-name-and-its-awesomeiii') - self.assertEqual(slug3, 'this-is-the-main-n\xe0m\xeb-and-its-aw\u03b5\u0161ome\u0131\u0131\u0131') + self.assertEqual(slug1, 'main-name-and-its-awesomeiiii-2012-02-18') + self.assertEqual(slug2, 'option-two-main-name-and-its-awesomeiiii') + self.assertEqual(slug3, 'this-is-the-main-n\xe0m\xeb-and-its-aw\u03b5\u0161ome\u0131\u0131\u0131i') # Stacked inlines ---------------------------------------------------- # Initial inline @@ -4526,11 +4526,12 @@ class SeleniumTests(AdminSeleniumTestCase): self.selenium.find_element_by_xpath('//input[@value="Save"]').click() self.assertEqual(MainPrepopulated.objects.all().count(), 1) MainPrepopulated.objects.get( - name=' this is the mAin nÀMë and it\'s awεšomeııı', + name=' this is the mAin nÀMë and it\'s awεšomeıııİ', pubdate='2012-02-18', status='option two', - slug1='main-name-and-its-awesomeiii-2012-02-18', - slug2='option-two-main-name-and-its-awesomeiii', + slug1='main-name-and-its-awesomeiiii-2012-02-18', + slug2='option-two-main-name-and-its-awesomeiiii', + slug3='this-is-the-main-nàmë-and-its-awεšomeıııi', ) self.assertEqual(RelatedPrepopulated.objects.all().count(), 4) RelatedPrepopulated.objects.get( diff --git a/tests/utils_tests/test_text.py b/tests/utils_tests/test_text.py index b904c228b9..66dbd5b366 100644 --- a/tests/utils_tests/test_text.py +++ b/tests/utils_tests/test_text.py @@ -196,6 +196,7 @@ class TestUtilsText(SimpleTestCase): ('foo ıç bar', 'foo-ıç-bar', True), (' foo ıç bar', 'foo-ıç-bar', True), ('你好', '你好', True), + ('İstanbul', 'istanbul', True), ) for value, output, is_unicode in items: self.assertEqual(text.slugify(value, allow_unicode=is_unicode), output)