favgroups: add stricter favgroup naming rules.
Don't allow favgroup names that: * Start or end with underscores. * Contain multiple underscores in a row. * Contain asterisks or non-printable characters. * Consist of only underscores. * Consist of only digits (conflicts with `favgroup:1234` syntax). Add a fix script that fixes favgroups that violate these rules and notifies the user.
This commit is contained in:
@@ -4,12 +4,10 @@ class FavoriteGroup < ApplicationRecord
|
|||||||
belongs_to :creator, class_name: "User"
|
belongs_to :creator, class_name: "User"
|
||||||
|
|
||||||
before_validation :normalize_name
|
before_validation :normalize_name
|
||||||
before_validation :strip_name
|
|
||||||
|
|
||||||
validates :name, presence: true
|
validates :name, presence: true
|
||||||
validates :name, uniqueness: { case_sensitive: false, scope: :creator_id }
|
validates :name, uniqueness: { case_sensitive: false, scope: :creator_id }
|
||||||
validates :name, format: { without: /,/, message: "cannot have commas" }
|
validate :validate_name, if: :name_changed?
|
||||||
validates :name, exclusion: { in: %w[any none], message: "can't be '%{value}'" }
|
|
||||||
validate :creator_can_create_favorite_groups, :on => :create
|
validate :creator_can_create_favorite_groups, :on => :create
|
||||||
validate :validate_number_of_posts
|
validate :validate_number_of_posts
|
||||||
validate :validate_posts
|
validate :validate_posts
|
||||||
@@ -104,8 +102,31 @@ class FavoriteGroup < ApplicationRecord
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def validate_name
|
||||||
|
case name
|
||||||
|
when /\A(any|none)\z/i
|
||||||
|
errors.add(:name, "cannot be '#{name}'")
|
||||||
|
when /,/
|
||||||
|
errors.add(:name, "cannot contain commas")
|
||||||
|
when /\*/
|
||||||
|
errors.add(:name, "cannot contain asterisks")
|
||||||
|
when /\A_/
|
||||||
|
errors.add(:name, "cannot begin with an underscore")
|
||||||
|
when /_\z/
|
||||||
|
errors.add(:name, "cannot end with an underscore")
|
||||||
|
when /__/
|
||||||
|
errors.add(:name, "cannot contain consecutive underscores")
|
||||||
|
when /[^[:graph:]]/
|
||||||
|
errors.add(:name, "cannot contain non-printable characters")
|
||||||
|
when ""
|
||||||
|
errors.add(:name, "cannot be blank")
|
||||||
|
when /\A[0-9]+\z/
|
||||||
|
errors.add(:name, "cannot contain only digits")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def self.normalize_name(name)
|
def self.normalize_name(name)
|
||||||
name.gsub(/[[:space:]]+/, "_")
|
name.gsub(/[_[:space:]]+/, "_").gsub(/\A_|_\z/, "")
|
||||||
end
|
end
|
||||||
|
|
||||||
def normalize_name
|
def normalize_name
|
||||||
@@ -128,10 +149,6 @@ class FavoriteGroup < ApplicationRecord
|
|||||||
find_by_name_or_id(name, user) or raise ActiveRecord::RecordNotFound
|
find_by_name_or_id(name, user) or raise ActiveRecord::RecordNotFound
|
||||||
end
|
end
|
||||||
|
|
||||||
def strip_name
|
|
||||||
self.name = name.to_s.strip
|
|
||||||
end
|
|
||||||
|
|
||||||
def pretty_name
|
def pretty_name
|
||||||
name&.tr("_", " ")
|
name&.tr("_", " ")
|
||||||
end
|
end
|
||||||
|
|||||||
66
script/fixes/129_fix_favgroup_names.rb
Executable file
66
script/fixes/129_fix_favgroup_names.rb
Executable file
@@ -0,0 +1,66 @@
|
|||||||
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
|
require_relative "base"
|
||||||
|
|
||||||
|
with_confirmation do
|
||||||
|
fix = ENV.fetch("FIX", "false").truthy?
|
||||||
|
|
||||||
|
users = User.where.associated(:favorite_groups).distinct
|
||||||
|
users.find_each do |user|
|
||||||
|
changed_favgroups = []
|
||||||
|
|
||||||
|
user.favorite_groups.each do |favgroup|
|
||||||
|
if favgroup.name.match?(/[^[:graph:]]/)
|
||||||
|
favgroup.name.gsub!(/[^[:graph:]]/, "_")
|
||||||
|
end
|
||||||
|
|
||||||
|
if favgroup.name.include?("*")
|
||||||
|
favgroup.name.tr!("*", "?")
|
||||||
|
end
|
||||||
|
|
||||||
|
if favgroup.name.include?("__")
|
||||||
|
favgroup.name.gsub!(/_+/, "_")
|
||||||
|
end
|
||||||
|
|
||||||
|
if favgroup.name.starts_with?("_")
|
||||||
|
favgroup.name.delete_prefix!("_")
|
||||||
|
end
|
||||||
|
|
||||||
|
if favgroup.name.ends_with?("_")
|
||||||
|
favgroup.name.delete_suffix!("_")
|
||||||
|
end
|
||||||
|
|
||||||
|
if favgroup.name.match?(/\A[0-9]+\z/)
|
||||||
|
favgroup.name = "favgroup_#{favgroup.name}"
|
||||||
|
end
|
||||||
|
|
||||||
|
if favgroup.name.blank? || user.favorite_groups.without(favgroup).exists?(name: favgroup.name)
|
||||||
|
favgroup.name = "favgroup_#{favgroup.id}"
|
||||||
|
end
|
||||||
|
|
||||||
|
if favgroup.changed?
|
||||||
|
changed_favgroups << favgroup
|
||||||
|
puts ({ id: favgroup.id, creator: favgroup.creator.name, changes: favgroup.changes }).to_json
|
||||||
|
favgroup.save! if fix
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if fix && changed_favgroups.present?
|
||||||
|
Dmail.create_automated(to: user, title: "Your favorite groups have been renamed", disable_email_notifications: true, body: <<~EOS)
|
||||||
|
The following #{"favgroup".pluralize(changed_favgroups.size)} #{changed_favgroups.one? ? "has" : "have"} been renamed:
|
||||||
|
|
||||||
|
#{changed_favgroups.map { |favgroup| "* favgroup ##{favgroup.id}: \"#{favgroup.name_was}\" -> \"#{favgroup.name}\"" }.join("\n")}
|
||||||
|
|
||||||
|
Your #{"favgroup".pluralize(changed_favgroups.size)} had to be renamed because #{changed_favgroups.one? ? "it" : "they"} didn't follow one of our new naming rules:
|
||||||
|
|
||||||
|
* Names can't consist of numbers only.
|
||||||
|
* Names can't start or end with an underscore (_).
|
||||||
|
* Names can't contain multiple underscores in a row (__).
|
||||||
|
* Names can't contain commas or asterisks (*).
|
||||||
|
* Names can't be blank.
|
||||||
|
|
||||||
|
You can change the name to something else by going to your "Favgroups":[/favorite_groups] page and clicking "Edit" to change the name.
|
||||||
|
EOS
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -58,4 +58,15 @@ class FavoriteGroupTest < ActiveSupport::TestCase
|
|||||||
assert_equal([], @fav_group.reload.post_ids)
|
assert_equal([], @fav_group.reload.post_ids)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "when validating names" do
|
||||||
|
subject { build(:favorite_group) }
|
||||||
|
|
||||||
|
should_not allow_value("foo,bar").for(:name)
|
||||||
|
should_not allow_value("foo*bar").for(:name)
|
||||||
|
should_not allow_value("123").for(:name)
|
||||||
|
should_not allow_value("_").for(:name)
|
||||||
|
should_not allow_value("any").for(:name)
|
||||||
|
should_not allow_value("none").for(:name)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user