store the slug as the title is, only sanitize the slug

and prettify code
This commit is contained in:
Erick Guan 2015-05-04 19:48:37 +08:00 committed by fantasticfears
parent b772ff6e13
commit a48dd1cc28
12 changed files with 45 additions and 45 deletions

View file

@ -224,7 +224,7 @@ Discourse.Category.reopenClass({
findSingleBySlug: function(slug) { findSingleBySlug: function(slug) {
return Discourse.Category.list().find(function(c) { return Discourse.Category.list().find(function(c) {
return Discourse.Category.slugFor(c) === slug || Discourse.Category.slugFor(c) === encodeURI(slug); return Discourse.Category.slugFor(c) === slug;
}); });
}, },
@ -254,9 +254,7 @@ Discourse.Category.reopenClass({
if (slug === 'none') { return parentCategory; } if (slug === 'none') { return parentCategory; }
category = categories.find(function(item) { category = categories.find(function(item) {
return item && item.get('parentCategory') === parentCategory && return item && item.get('parentCategory') === parentCategory && Discourse.Category.slugFor(item) === (parentSlug + "/" + slug);
(Discourse.Category.slugFor(item) === (parentSlug + "/" + slug) ||
Discourse.Category.slugFor(item) === encodeURIComponent(parentSlug) + "/" + encodeURIComponent(slug));
}); });
} }
} else { } else {

View file

@ -427,8 +427,7 @@ class TopicsController < ApplicationController
end end
def slugs_do_not_match def slugs_do_not_match
params[:slug] && (@topic_view.topic.slug != params[:slug] && params[:slug] && @topic_view.topic.slug != params[:slug]
@topic_view.topic.slug != Rack::Utils.escape_path(params[:slug])) # maybe percent encoded
end end
def redirect_to_correct_topic(topic, post_number=nil) def redirect_to_correct_topic(topic, post_number=nil)

View file

@ -207,7 +207,7 @@ SQL
if slug.present? if slug.present?
# santized custom slug # santized custom slug
self.slug = Slug.for(Rack::Utils.unescape(slug), '') self.slug = Slug.for(slug, '')
errors.add(:slug, 'is already in use') if duplicate_slug? errors.add(:slug, 'is already in use') if duplicate_slug?
else else
# auto slug # auto slug
@ -342,13 +342,11 @@ SQL
def self.query_parent_category(parent_slug) def self.query_parent_category(parent_slug)
self.where(slug: parent_slug, parent_category_id: nil).pluck(:id).first || self.where(slug: parent_slug, parent_category_id: nil).pluck(:id).first ||
self.where(slug: Rack::Utils.escape_path(parent_slug), parent_category_id: nil).pluck(:id).first ||
self.where(id: parent_slug.to_i).pluck(:id).first self.where(id: parent_slug.to_i).pluck(:id).first
end end
def self.query_category(slug_or_id, parent_category_id) def self.query_category(slug_or_id, parent_category_id)
self.where(slug: slug_or_id, parent_category_id: parent_category_id).includes(:featured_users).first || self.where(slug: slug_or_id, parent_category_id: parent_category_id).includes(:featured_users).first ||
self.where(slug: Rack::Utils.escape_path(slug_or_id), parent_category_id: parent_category_id).includes(:featured_users).first ||
self.where(id: slug_or_id.to_i, parent_category_id: parent_category_id).includes(:featured_users).first self.where(id: slug_or_id.to_i, parent_category_id: parent_category_id).includes(:featured_users).first
end end

View file

@ -665,7 +665,7 @@ class Topic < ActiveRecord::Base
def slug def slug
unless slug = read_attribute(:slug) unless slug = read_attribute(:slug)
return '' unless title.present? return '' unless title.present?
slug = Slug.for(title, 'topic') slug = Slug.for(title)
if new_record? if new_record?
write_attribute(:slug, slug) write_attribute(:slug, slug)
else else
@ -677,7 +677,7 @@ class Topic < ActiveRecord::Base
end end
def title=(t) def title=(t)
slug = Slug.for(t.to_s, 'topic') slug = Slug.for(t.to_s)
write_attribute(:slug, slug) write_attribute(:slug, slug)
write_attribute(:title,t) write_attribute(:title,t)
end end

View file

@ -10,7 +10,7 @@ class NotificationSerializer < ApplicationSerializer
:is_warning :is_warning
def slug def slug
Slug.for(object.topic.title, 'topic') if object.topic.present? Slug.for(object.topic.title) if object.topic.present?
end end
def is_warning def is_warning

View file

@ -58,7 +58,7 @@ class UserActionSerializer < ApplicationSerializer
end end
def slug def slug
Slug.for(object.title, 'topic') Slug.for(object.title)
end end
def include_slug? def include_slug?

View file

@ -48,7 +48,7 @@ module Onebox
return linked unless Guardian.new.can_see?(post) return linked unless Guardian.new.can_see?(post)
topic = post.topic topic = post.topic
slug = Slug.for(topic.title, 'topic') slug = Slug.for(topic.title)
excerpt = post.excerpt(SiteSetting.post_onebox_maxlength) excerpt = post.excerpt(SiteSetting.post_onebox_maxlength)
excerpt.gsub!("\n"," ") excerpt.gsub!("\n"," ")

View file

@ -7,7 +7,6 @@ module Slug
when :ascii then self.ascii_generator(string) when :ascii then self.ascii_generator(string)
when :encoded then self.encoded_generator(string) when :encoded then self.encoded_generator(string)
when :none then self.none_generator(string) when :none then self.none_generator(string)
else raise Discourse::SiteSettingMissing
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 : '')
@ -17,15 +16,20 @@ module Slug
private private
def self.ascii_generator(string) def self.ascii_generator(string)
slug = string.gsub("'", "").parameterize string.gsub("'", "")
slug.gsub("_", "-") .parameterize
.gsub("_", "-")
end end
def self.encoded_generator(string) def self.encoded_generator(string)
# strip and sanitized RFC 3986 reserved character and blank # This generator will sanitize almost all special characters,
string = string.strip.gsub(/\s+/, '-').gsub(/[:\/\?#\[\]@!\$\s&'\(\)\*\+,;=]+/, '') # including reserved characters from RFC3986.
string = Rack::Utils.escape_path(string) # See also URI::REGEXP::PATTERN.
string =~ /^-+$/ ? '' : string string.strip
.gsub(/\s+/, '-')
.gsub(/[:\/\?#\[\]@!\$&'\(\)\*\+,;=_\.~%\\`^\s|\{\}"<>]+/, '') # :/?#[]@!$&'()*+,;=_.~%\`^|{}"<>
.gsub(/\A-+|-+\z/, '') # remove possible trailing and preceding dashes
.squeeze('-') # squeeze continuous dashes to prettify slug
end end
def self.none_generator(string) def self.none_generator(string)

View file

@ -25,7 +25,7 @@ task 'build_test_topic' => :environment do
first_post = PostCreator.new(evil_trout, raw: "This is the original post.", title: "pushState/replaceState test topic").create first_post = PostCreator.new(evil_trout, raw: "This is the original post.", title: "pushState/replaceState test topic").create
topic = first_post.topic topic = first_post.topic
topic_url = "#{Discourse.base_url}/t/#{Slug.for(topic.title, 'topic')}/#{topic.id}" topic_url = "#{Discourse.base_url}/t/#{Slug.for(topic.title)}/#{topic.id}"
99.times do |i| 99.times do |i|
post_number = (i + 2) post_number = (i + 2)

View file

@ -10,15 +10,15 @@ describe Slug do
before { SiteSetting.slug_generation_method = 'ascii' } before { SiteSetting.slug_generation_method = 'ascii' }
it 'generates the slug' do it 'generates the slug' do
expect(Slug.for("hello world", 'topic')).to eq('hello-world') expect(Slug.for("hello world")).to eq('hello-world')
end end
it 'generates default slug when nothing' do it 'generates default slug when nothing' do
expect(Slug.for('', 'topic')).to eq('topic') expect(Slug.for('')).to eq('topic')
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', 'topic')).to eq('topic') expect(Slug.for('123')).to eq('topic')
end end
end end
@ -27,15 +27,15 @@ describe Slug do
after { SiteSetting.slug_generation_method = 'ascii' } after { SiteSetting.slug_generation_method = 'ascii' }
it 'generates the slug' do it 'generates the slug' do
expect(Slug.for("熱帶風暴畫眉", 'topic')).to eq('%E7%86%B1%E5%B8%B6%E9%A2%A8%E6%9A%B4%E7%95%AB%E7%9C%89') expect(Slug.for("熱帶風暴畫眉")).to eq('熱帶風暴畫眉')
end end
it 'generates default slug when nothing' do it 'generates default slug when nothing' do
expect(Slug.for('', 'topic')).to eq('topic') expect(Slug.for('')).to eq('topic')
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', 'topic')).to eq('topic') expect(Slug.for('123')).to eq('topic')
end end
end end
@ -45,9 +45,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", 'topic')).to eq('topic') expect(Slug.for("hello world")).to eq('topic')
expect(Slug.for('', 'topic')).to eq('topic') expect(Slug.for('')).to eq('topic')
expect(Slug.for('123', 'topic')).to eq('topic') expect(Slug.for('123')).to eq('topic')
end end
end end
end end
@ -111,14 +111,15 @@ 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-%7E-_%7Cthis") expect(Slug.encoded_generator("Jeff hate's !~-_|,=#this")).to eq("Jeff-hates-this")
expect(Slug.encoded_generator("뉴스피드")).to eq("%EB%89%B4%EC%8A%A4%ED%94%BC%EB%93%9C") expect(Slug.encoded_generator("뉴스피드")).to eq("뉴스피드")
expect(Slug.encoded_generator("آموزش اضافه کردن لینک اختیاری به هدر")).to eq("%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%A7%D8%B6%D8%A7%D9%81%D9%87-%DA%A9%D8%B1%D8%AF%D9%86-%D9%84%DB%8C%D9%86%DA%A9-%D8%A7%D8%AE%D8%AA%DB%8C%D8%A7%D8%B1%DB%8C-%D8%A8%D9%87-%D9%87%D8%AF%D8%B1") expect(Slug.encoded_generator("آموزش اضافه کردن لینک اختیاری به هدر")).to eq("آموزش-اضافه-کردن-لینک-اختیاری-به-هدر")
expect(Slug.encoded_generator("熱帶風暴畫眉")).to eq("%E7%86%B1%E5%B8%B6%E9%A2%A8%E6%9A%B4%E7%95%AB%E7%9C%89") 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("")
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

View file

@ -209,8 +209,8 @@ describe Category do
end end
it "creates a slug" do it "creates a slug" do
expect(@category.slug).to eq("%E6%B5%8B%E8%AF%95") expect(@category.slug).to eq("测试")
expect(@category.slug_for_url).to eq("%E6%B5%8B%E8%AF%95") expect(@category.slug_for_url).to eq("测试")
end end
end end
end end

View file

@ -19,15 +19,15 @@ describe Topic do
after { SiteSetting.slug_generation_method = 'ascii' } after { SiteSetting.slug_generation_method = 'ascii' }
it "returns a Slug for a title" do it "returns a Slug for a title" do
Slug.expects(:for).with(title, 'topic').returns(slug) Slug.expects(:for).with(title).returns(slug)
expect(Fabricate.build(:topic, title: title).slug).to eq(slug) expect(Fabricate.build(:topic, title: title).slug).to eq(slug)
end end
context 'for cjk characters' do context 'for cjk characters' do
let(:title) { "熱帶風暴畫眉" } let(:title) { "熱帶風暴畫眉" }
let(:slug) { "%E7%86%B1%E5%B8%B6%E9%A2%A8%E6%9A%B4%E7%95%AB%E7%9C%89" } let(:slug) { "熱帶風暴畫眉" }
it "returns encoded Slug for a title" do it "returns encoded Slug for a title" do
Slug.expects(:for).with(title, 'topic').returns(slug) Slug.expects(:for).with(title).returns(slug)
expect(Fabricate.build(:topic, title: title).slug).to eq(slug) expect(Fabricate.build(:topic, title: title).slug).to eq(slug)
end end
end end
@ -36,7 +36,7 @@ describe Topic do
let(:title) { "123456789" } let(:title) { "123456789" }
let(:slug) { "topic" } let(:slug) { "topic" }
it 'generates default slug' do it 'generates default slug' do
Slug.expects(:for).with(title, 'topic').returns("topic") Slug.expects(:for).with(title).returns("topic")
expect(Fabricate.build(:topic, title: title).slug).to eq("topic") expect(Fabricate.build(:topic, title: title).slug).to eq("topic")
end end
end end
@ -49,7 +49,7 @@ describe Topic do
let(:slug) { "topic" } let(:slug) { "topic" }
it "returns a Slug for a title" do it "returns a Slug for a title" do
Slug.expects(:for).with(title, 'topic').returns('topic') Slug.expects(:for).with(title).returns('topic')
expect(Fabricate.build(:topic, title: title).slug).to eq(slug) expect(Fabricate.build(:topic, title: title).slug).to eq(slug)
end end
end end
@ -57,7 +57,7 @@ describe Topic do
context '#ascii_generator' do context '#ascii_generator' do
before { SiteSetting.slug_generation_method = 'ascii' } before { SiteSetting.slug_generation_method = 'ascii' }
it "returns a Slug for a title" do it "returns a Slug for a title" do
Slug.expects(:for).with(title, 'topic').returns(slug) Slug.expects(:for).with(title).returns(slug)
expect(Fabricate.build(:topic, title: title).slug).to eq(slug) expect(Fabricate.build(:topic, title: title).slug).to eq(slug)
end end
@ -65,7 +65,7 @@ describe Topic do
let(:title) { "熱帶風暴畫眉" } let(:title) { "熱帶風暴畫眉" }
let(:slug) { 'topic' } let(:slug) { 'topic' }
it "returns 'topic' when the slug is empty (say, non-latin characters)" do it "returns 'topic' when the slug is empty (say, non-latin characters)" do
Slug.expects(:for).with(title, 'topic').returns("topic") Slug.expects(:for).with(title).returns("topic")
expect(Fabricate.build(:topic, title: title).slug).to eq("topic") expect(Fabricate.build(:topic, title: title).slug).to eq("topic")
end end
end end