Unify website slugify method behavior with or without python-slugify, and add some unit tests
This commit is contained in:
parent
003e910e61
commit
b4afff3618
|
@ -8,6 +8,7 @@ import itertools
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
import mimetypes
|
import mimetypes
|
||||||
|
import unicodedata
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import urlparse
|
import urlparse
|
||||||
|
@ -28,6 +29,7 @@ except ImportError:
|
||||||
import openerp
|
import openerp
|
||||||
from openerp.osv import orm, osv, fields
|
from openerp.osv import orm, osv, fields
|
||||||
from openerp.tools import html_escape as escape
|
from openerp.tools import html_escape as escape
|
||||||
|
from openerp.tools import ustr as ustr
|
||||||
from openerp.tools.safe_eval import safe_eval
|
from openerp.tools.safe_eval import safe_eval
|
||||||
from openerp.addons.web.http import request
|
from openerp.addons.web.http import request
|
||||||
|
|
||||||
|
@ -85,15 +87,29 @@ def is_multilang_url(local_url, langs=None):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def slugify(s, max_length=None):
|
def slugify(s, max_length=None):
|
||||||
|
""" Transform a string to a slug that can be used in a url path.
|
||||||
|
|
||||||
|
This method will first try to do the job with python-slugify if present.
|
||||||
|
Otherwise it will process string by stripping leading and ending spaces,
|
||||||
|
converting unicode chars to ascii, lowering all chars and replacing spaces
|
||||||
|
and underscore with hyphen "-".
|
||||||
|
|
||||||
|
:param s: str
|
||||||
|
:param max_length: int
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
s = ustr(s)
|
||||||
if slugify_lib:
|
if slugify_lib:
|
||||||
# There are 2 different libraries only python-slugify is supported
|
# There are 2 different libraries only python-slugify is supported
|
||||||
try:
|
try:
|
||||||
return slugify_lib.slugify(s, max_length=max_length)
|
return slugify_lib.slugify(s, max_length=max_length)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
pass
|
pass
|
||||||
spaceless = re.sub(r'\s+', '-', s)
|
uni = unicodedata.normalize('NFKD', s).encode('ascii', 'ignore').decode('ascii')
|
||||||
specialless = re.sub(r'[^-_A-Za-z0-9]', '', spaceless)
|
slug = re.sub('[\W_]', ' ', uni).strip().lower()
|
||||||
return specialless[:max_length]
|
slug = re.sub('[-\s]+', '-', slug)
|
||||||
|
|
||||||
|
return slug[:max_length]
|
||||||
|
|
||||||
def slug(value):
|
def slug(value):
|
||||||
if isinstance(value, orm.browse_record):
|
if isinstance(value, orm.browse_record):
|
||||||
|
@ -147,7 +163,7 @@ class website(osv.osv):
|
||||||
_defaults = {
|
_defaults = {
|
||||||
'company_id': lambda self,cr,uid,c: self.pool['ir.model.data'].xmlid_to_res_id(cr, openerp.SUPERUSER_ID, 'base.public_user'),
|
'company_id': lambda self,cr,uid,c: self.pool['ir.model.data'].xmlid_to_res_id(cr, openerp.SUPERUSER_ID, 'base.public_user'),
|
||||||
}
|
}
|
||||||
|
|
||||||
# cf. Wizard hack in website_views.xml
|
# cf. Wizard hack in website_views.xml
|
||||||
def noop(self, *args, **kwargs):
|
def noop(self, *args, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -9,6 +9,7 @@ from lxml.builder import E
|
||||||
from openerp.tests import common
|
from openerp.tests import common
|
||||||
from openerp.addons.base.ir import ir_qweb
|
from openerp.addons.base.ir import ir_qweb
|
||||||
from openerp.addons.website.models.ir_qweb import html_to_text
|
from openerp.addons.website.models.ir_qweb import html_to_text
|
||||||
|
from openerp.addons.website.models.website import slugify
|
||||||
|
|
||||||
impl = getDOMImplementation()
|
impl = getDOMImplementation()
|
||||||
document = impl.createDocument(None, None, None)
|
document = impl.createDocument(None, None, None)
|
||||||
|
@ -238,3 +239,56 @@ class TestConvertBack(common.TransactionCase):
|
||||||
"New content",
|
"New content",
|
||||||
"element edition should have been written directly to the m2o record"
|
"element edition should have been written directly to the m2o record"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class TestTitleToSlug(unittest2.TestCase):
|
||||||
|
"""
|
||||||
|
Those tests should pass with or without python-slugify
|
||||||
|
See website/models/website.py slugify method
|
||||||
|
"""
|
||||||
|
def test_spaces(self):
|
||||||
|
self.assertEqual(
|
||||||
|
"spaces",
|
||||||
|
slugify(u" spaces ")
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_unicode(self):
|
||||||
|
self.assertEqual(
|
||||||
|
"heterogeneite",
|
||||||
|
slugify(u"hétérogénéité")
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_underscore(self):
|
||||||
|
self.assertEqual(
|
||||||
|
"one-two",
|
||||||
|
slugify(u"one_two")
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_caps(self):
|
||||||
|
self.assertEqual(
|
||||||
|
"camelcase",
|
||||||
|
slugify(u"CamelCase")
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_special_chars(self):
|
||||||
|
self.assertEqual(
|
||||||
|
"o-d-o-o",
|
||||||
|
slugify(u"o!#d{|\o/@~o&%^?")
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_str_to_unicode(self):
|
||||||
|
self.assertEqual(
|
||||||
|
"espana",
|
||||||
|
slugify("España")
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_numbers(self):
|
||||||
|
self.assertEqual(
|
||||||
|
"article-1",
|
||||||
|
slugify(u"Article 1")
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_all(self):
|
||||||
|
self.assertEqual(
|
||||||
|
"do-you-know-martine-a-la-plage",
|
||||||
|
slugify(u"Do YOU know 'Martine à la plage' ?")
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in New Issue