2013-02-06 20:09:31 -05:00
|
|
|
require 'iconv'
|
|
|
|
|
|
|
|
#
|
|
|
|
# Given a string, tell us whether or not is acceptable. Also, remove stuff we don't like
|
|
|
|
# such as leading / trailing space.
|
|
|
|
#
|
|
|
|
class TextSentinel
|
|
|
|
|
|
|
|
attr_accessor :text
|
|
|
|
|
|
|
|
def self.non_symbols_regexp
|
|
|
|
/[\ -\/\[-\`\:-\@\{-\~]/m
|
|
|
|
end
|
|
|
|
|
|
|
|
def initialize(text, opts=nil)
|
|
|
|
if text.present?
|
|
|
|
@text = Iconv.new('UTF-8//IGNORE', 'UTF-8').iconv(text.dup)
|
|
|
|
end
|
|
|
|
|
|
|
|
@opts = opts || {}
|
|
|
|
|
|
|
|
if @text.present?
|
|
|
|
@text.strip!
|
|
|
|
@text.gsub!(/ +/m, ' ') if @opts[:remove_interior_spaces]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-02-08 16:55:40 -05:00
|
|
|
def self.title_sentinel(text)
|
|
|
|
TextSentinel.new(text,
|
|
|
|
min_entropy: SiteSetting.title_min_entropy,
|
|
|
|
max_word_length: SiteSetting.max_word_length,
|
|
|
|
remove_interior_spaces: true)
|
|
|
|
end
|
|
|
|
|
2013-02-06 20:09:31 -05:00
|
|
|
# Entropy is a number of how many unique characters the string needs.
|
|
|
|
def entropy
|
|
|
|
return 0 if @text.blank?
|
|
|
|
@entropy ||= @text.each_char.to_a.uniq.size
|
|
|
|
end
|
|
|
|
|
|
|
|
def valid?
|
|
|
|
|
|
|
|
# Blank strings are not valid
|
|
|
|
return false if @text.blank?
|
|
|
|
|
|
|
|
# Entropy check if required
|
|
|
|
return false if @opts[:min_entropy].present? and (entropy < @opts[:min_entropy])
|
|
|
|
|
|
|
|
# We don't have a comprehensive list of symbols, but this will eliminate some noise
|
|
|
|
non_symbols = @text.gsub(TextSentinel.non_symbols_regexp, '').size
|
|
|
|
return false if non_symbols == 0
|
|
|
|
|
|
|
|
# Don't allow super long strings without spaces
|
|
|
|
|
|
|
|
return false if @opts[:max_word_length] and @text =~ /\w{#{@opts[:max_word_length]},}(\s|$)/
|
|
|
|
|
|
|
|
# We don't allow all upper case content
|
|
|
|
return false if @text == @text.upcase
|
|
|
|
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|