mirror of
https://github.com/discourse/discourse.git
synced 2024-11-23 01:16:38 -06:00
Downcase encoded slug by default and more specs
This commit is contained in:
parent
c650ef9138
commit
7c3123a2dd
35
lib/slug.rb
35
lib/slug.rb
@ -3,8 +3,9 @@
|
|||||||
module Slug
|
module Slug
|
||||||
|
|
||||||
CHAR_FILTER_REGEXP = /[:\/\?#\[\]@!\$&'\(\)\*\+,;=_\.~%\\`^\s|\{\}"<>]+/ # :/?#[]@!$&'()*+,;=_.~%\`^|{}"<>
|
CHAR_FILTER_REGEXP = /[:\/\?#\[\]@!\$&'\(\)\*\+,;=_\.~%\\`^\s|\{\}"<>]+/ # :/?#[]@!$&'()*+,;=_.~%\`^|{}"<>
|
||||||
|
MAX_LENGTH = 255
|
||||||
|
|
||||||
def self.for(string, default = 'topic')
|
def self.for(string, default = 'topic', max_length = MAX_LENGTH)
|
||||||
slug =
|
slug =
|
||||||
case (SiteSetting.slug_generation_method || :ascii).to_sym
|
case (SiteSetting.slug_generation_method || :ascii).to_sym
|
||||||
when :ascii then self.ascii_generator(string)
|
when :ascii then self.ascii_generator(string)
|
||||||
@ -13,30 +14,38 @@ module Slug
|
|||||||
end
|
end
|
||||||
# Reject slugs that only contain numbers, because they would be indistinguishable from id's.
|
# Reject slugs that only contain numbers, because they would be indistinguishable from id's.
|
||||||
slug = (slug =~ /[^\d]/ ? slug : '')
|
slug = (slug =~ /[^\d]/ ? slug : '')
|
||||||
|
slug = self.prettify_slug(slug, max_length: max_length)
|
||||||
slug.blank? ? default : slug
|
slug.blank? ? default : slug
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.sanitize(string)
|
def self.sanitize(string, downcase: false, max_length: MAX_LENGTH)
|
||||||
self.encoded_generator(string)
|
slug = self.encoded_generator(string, downcase: downcase)
|
||||||
|
self.prettify_slug(slug, max_length: max_length)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def self.ascii_generator(string)
|
def self.prettify_slug(slug, max_length: MAX_LENGTH)
|
||||||
string.tr("'", "")
|
slug.tr!("_", "-")
|
||||||
.parameterize
|
slug = slug.squeeze('-') # squeeze continuous dashes to prettify slug
|
||||||
.tr("_", "-")
|
slug.gsub!(/\A-+|-+\z/, '') # remove possible trailing and preceding dashes
|
||||||
|
slug.truncate(max_length, omission: '')
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.encoded_generator(string)
|
def self.ascii_generator(string)
|
||||||
|
string.tr!("'", "")
|
||||||
|
string.parameterize
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.encoded_generator(string, downcase: true)
|
||||||
# This generator will sanitize almost all special characters,
|
# This generator will sanitize almost all special characters,
|
||||||
# including reserved characters from RFC3986.
|
# including reserved characters from RFC3986.
|
||||||
# See also URI::REGEXP::PATTERN.
|
# See also URI::REGEXP::PATTERN.
|
||||||
string.strip
|
string.strip!
|
||||||
.gsub(/\s+/, '-')
|
string.gsub!(/\s+/, '-')
|
||||||
.gsub(CHAR_FILTER_REGEXP, '')
|
string.gsub!(CHAR_FILTER_REGEXP, '')
|
||||||
.gsub(/\A-+|-+\z/, '') # remove possible trailing and preceding dashes
|
string.downcase! if downcase
|
||||||
.squeeze('-') # squeeze continuous dashes to prettify slug
|
string
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.none_generator(string)
|
def self.none_generator(string)
|
||||||
|
@ -6,6 +6,24 @@ require 'slug'
|
|||||||
describe Slug do
|
describe Slug do
|
||||||
|
|
||||||
describe '#for' do
|
describe '#for' do
|
||||||
|
let(:default_slug) { 'topic' }
|
||||||
|
|
||||||
|
let(:very_long_string) do
|
||||||
|
'内容似乎不清晰,这是个完整的句子吗?内容似乎不清晰,这是个完整的句子吗?' * 10
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns topic by default' do
|
||||||
|
expect(Slug.for('')).to eq default_slug
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'accepts fallback' do
|
||||||
|
expect(Slug.for('', 'king')).to eq 'king'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'replaces the underscore' do
|
||||||
|
expect(Slug.for("o_o_o")).to eq("o-o-o")
|
||||||
|
end
|
||||||
|
|
||||||
context 'ascii generator' do
|
context 'ascii generator' do
|
||||||
before { SiteSetting.slug_generation_method = 'ascii' }
|
before { SiteSetting.slug_generation_method = 'ascii' }
|
||||||
|
|
||||||
@ -14,11 +32,15 @@ describe Slug do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it 'generates default slug when nothing' do
|
it 'generates default slug when nothing' do
|
||||||
expect(Slug.for('')).to eq('topic')
|
expect(Slug.for('')).to eq(default_slug)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't generate slugs that are just numbers" do
|
it "doesn't generate slugs that are just numbers" do
|
||||||
expect(Slug.for('123')).to eq('topic')
|
expect(Slug.for('123')).to eq(default_slug)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "fallbacks to empty string if it's too long" do
|
||||||
|
expect(Slug.for(very_long_string)).to eq(default_slug)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -28,14 +50,25 @@ describe Slug do
|
|||||||
|
|
||||||
it 'generates the slug' do
|
it 'generates the slug' do
|
||||||
expect(Slug.for("熱帶風暴畫眉")).to eq('熱帶風暴畫眉')
|
expect(Slug.for("熱帶風暴畫眉")).to eq('熱帶風暴畫眉')
|
||||||
|
expect(Slug.for("Jeff hate's !~-_|,=#this")).to eq("jeff-hates-this")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'generates default slug when nothing' do
|
it 'generates default slug when nothing' do
|
||||||
expect(Slug.for('')).to eq('topic')
|
expect(Slug.for('')).to eq(default_slug)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't generate slugs that are just numbers" do
|
it "doesn't generate slugs that are just numbers" do
|
||||||
expect(Slug.for('123')).to eq('topic')
|
expect(Slug.for('123')).to eq(default_slug)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "handles the special characters" do
|
||||||
|
expect(Slug.for(
|
||||||
|
" - English and Chinese title with special characters / 中文标题 !@:?\\:'`#^& $%&*()` -- "
|
||||||
|
)).to eq("english-and-chinese-title-with-special-characters-中文标题")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "kills the trailing dash" do
|
||||||
|
expect(Slug.for("2- -this!~-_|,we-#-=^-")).to eq('2-this-we')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -45,9 +78,9 @@ describe Slug do
|
|||||||
|
|
||||||
it 'generates the slug' do
|
it 'generates the slug' do
|
||||||
expect(Slug.for("hello world", 'category')).to eq('category')
|
expect(Slug.for("hello world", 'category')).to eq('category')
|
||||||
expect(Slug.for("hello world")).to eq('topic')
|
expect(Slug.for("hello world")).to eq(default_slug)
|
||||||
expect(Slug.for('')).to eq('topic')
|
expect(Slug.for('')).to eq(default_slug)
|
||||||
expect(Slug.for('123')).to eq('topic')
|
expect(Slug.for('123')).to eq(default_slug)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -89,10 +122,6 @@ describe Slug do
|
|||||||
expect(Slug.ascii_generator(from)).to eq(to)
|
expect(Slug.ascii_generator(from)).to eq(to)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'replaces underscores' do
|
|
||||||
expect(Slug.ascii_generator("o_o_o")).to eq("o-o-o")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "doesn't keep single quotes within word" do
|
it "doesn't keep single quotes within word" do
|
||||||
expect(Slug.ascii_generator("Jeff hate's this")).to eq("jeff-hates-this")
|
expect(Slug.ascii_generator("Jeff hate's this")).to eq("jeff-hates-this")
|
||||||
end
|
end
|
||||||
@ -111,15 +140,13 @@ describe Slug do
|
|||||||
after { SiteSetting.slug_generation_method = 'ascii' }
|
after { SiteSetting.slug_generation_method = 'ascii' }
|
||||||
|
|
||||||
it 'generates precentage encoded string' do
|
it 'generates precentage encoded string' do
|
||||||
expect(Slug.encoded_generator("Jeff hate's !~-_|,=#this")).to eq("Jeff-hates-this")
|
|
||||||
expect(Slug.encoded_generator("뉴스피드")).to eq("뉴스피드")
|
expect(Slug.encoded_generator("뉴스피드")).to eq("뉴스피드")
|
||||||
expect(Slug.encoded_generator("آموزش اضافه کردن لینک اختیاری به هدر")).to eq("آموزش-اضافه-کردن-لینک-اختیاری-به-هدر")
|
expect(Slug.encoded_generator("آموزش اضافه کردن لینک اختیاری به هدر")).to eq("آموزش-اضافه-کردن-لینک-اختیاری-به-هدر")
|
||||||
expect(Slug.encoded_generator("熱帶風暴畫眉")).to eq("熱帶風暴畫眉")
|
expect(Slug.encoded_generator("熱帶風暴畫眉")).to eq("熱帶風暴畫眉")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'reject RFC 3986 reserved character and blank' do
|
it 'reject RFC 3986 reserved character and blank' do
|
||||||
expect(Slug.encoded_generator(":/?#[]@!$ &'()*+,;=% -_`~.")).to eq("")
|
expect(Slug.encoded_generator(":/?#[]@!$ &'()*+,;=% -_`~.")).to eq("---") # will be clear by #for
|
||||||
expect(Slug.encoded_generator(" - English and Chinese title with special characters / 中文标题 !@:?\\:'`#^& $%&*()` -- ")).to eq("English-and-Chinese-title-with-special-characters-中文标题")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'generates null when nothing' do
|
it 'generates null when nothing' do
|
||||||
@ -129,6 +156,10 @@ describe Slug do
|
|||||||
it "keeps number unchanged" do
|
it "keeps number unchanged" do
|
||||||
expect(Slug.encoded_generator('123')).to eq('123')
|
expect(Slug.encoded_generator('123')).to eq('123')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'downcase the string' do
|
||||||
|
expect(Slug.encoded_generator("LoWer")).to eq('lower')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#none_generator' do
|
describe '#none_generator' do
|
||||||
|
@ -338,7 +338,7 @@ describe CategoriesController do
|
|||||||
expect(@category.reload.slug).to eq('valid-slug')
|
expect(@category.reload.slug).to eq('valid-slug')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'accepts and sanitize custom slug when the slug generation method is not english' do
|
it 'accepts and sanitize custom slug when the slug generation method is not ascii' do
|
||||||
SiteSetting.slug_generation_method = 'none'
|
SiteSetting.slug_generation_method = 'none'
|
||||||
put :update_slug,
|
put :update_slug,
|
||||||
params: { category_id: @category.id, slug: ' another !_ slug @' },
|
params: { category_id: @category.id, slug: ' another !_ slug @' },
|
||||||
|
Loading…
Reference in New Issue
Block a user