diff --git a/app/logical/tag_name_validator.rb b/app/logical/tag_name_validator.rb new file mode 100644 index 000000000..872b9861d --- /dev/null +++ b/app/logical/tag_name_validator.rb @@ -0,0 +1,30 @@ +class TagNameValidator < ActiveModel::EachValidator + def validate_each(record, attribute, value) + case Tag.normalize_name(value) + when /\A_*\z/ + record.errors[attribute] << "'#{value}' cannot be blank" + when /\*/ + record.errors[attribute] << "'#{value}' cannot contain asterisks ('*')" + when /,/ + record.errors[attribute] << "'#{value}' cannot contain commas (',')" + when /\A~/ + record.errors[attribute] << "'#{value}' cannot begin with a tilde ('~')" + when /\A-/ + record.errors[attribute] << "'#{value}' cannot begin with a dash ('-')" + when /\A_/ + record.errors[attribute] << "'#{value}' cannot begin with an underscore" + when /_\z/ + record.errors[attribute] << "'#{value}' cannot end with an underscore" + when /__/ + record.errors[attribute] << "'#{value}' cannot contain consecutive underscores" + when /[^[[:graph:]]]/ + record.errors[attribute] << "'#{value}' cannot contain non-printable characters" + when /[^[[:ascii:]]]/ + record.errors[attribute] << "'#{value}' must consist of only ASCII characters" + when /\A(#{Tag::METATAGS}|#{Tag::SUBQUERY_METATAGS}):(.+)\z/i + record.errors[attribute] << "'#{value}' cannot begin with '#{$1}:'" + when /\A(#{Tag.categories.regexp}):(.+)\z/i + record.errors[attribute] << "'#{value}' cannot begin with '#{$1}:'" + end + end +end diff --git a/app/models/tag.rb b/app/models/tag.rb index 445c001f2..301b89f1b 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -6,6 +6,8 @@ class Tag < ActiveRecord::Base attr_accessible :is_locked, :as => [:moderator, :admin] has_one :wiki_page, :foreign_key => "title", :primary_key => "name" + validates :name, uniqueness: true, tag_name: true, on: :create + module ApiMethods def to_legacy_json return { @@ -179,7 +181,7 @@ class Tag < ActiveRecord::Base module NameMethods def normalize_name(name) - name.mb_chars.downcase.tr(" ", "_").gsub(/\A[-~]+/, "").gsub(/\*/, "").to_s + name.to_s.mb_chars.downcase.strip.tr(" ", "_").to_s end def find_or_create_by_name(name, options = {})