# # 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) @opts = opts || {} if text.present? @text = text.encode('UTF-8', invalid: :replace, undef: :replace, replace: '') @text.gsub!(/ +/m, ' ') if @opts[:remove_interior_spaces] @text.strip! if @opts[:strip] end end 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, strip: true) end # Entropy is a number of how many unique characters the string needs. def entropy return 0 if @text.blank? @entropy ||= @text.strip.each_char.to_a.uniq.size end def valid? # Blank strings are not valid return false if @text.blank? || @text.strip.blank? # Entropy check if required return false if @opts[:min_entropy].present? && (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] && @text =~ /\w{#{@opts[:max_word_length]},}(\s|$)/ # We don't allow all upper case content in english return false if (@text =~ /[A-Z]+/) && (@text == @text.upcase) true end end