diff --git a/app/models/tag.rb b/app/models/tag.rb index 207d77d73..d985a41cc 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -23,8 +23,10 @@ class Tag < ApplicationRecord validates :name, tag_name: true, uniqueness: true, on: :create validates :name, tag_name: true, on: :name validates :category, inclusion: { in: TagCategory.category_ids } + validate :validate_category, if: :category_changed? before_create :create_character_tag_for_cosplay_tag, if: :is_cosplay_tag? + after_save :update_tag_alias_categories, if: :saved_change_to_category? after_save :update_category_cache, if: :saved_change_to_category? after_save :update_category_post_counts, if: :saved_change_to_category? @@ -173,6 +175,21 @@ class Tag < ApplicationRecord def update_category_cache Cache.put("tc:#{Cache.hash(name)}", category, 3.hours) end + + # When a tag's category is changed, also change the categories of any aliases pointing to it. + def update_tag_alias_categories + consequent_aliases.each do |tag_alias| + if tag_alias.antecedent_tag.category != category + tag_alias.antecedent_tag.update!(category: category, updater: updater) + end + end + end + + def validate_category + if is_aliased? && category != aliased_tag.category + errors.add(:base, "Can't change the category of an aliased tag") + end + end end concerning :NameMethods do diff --git a/script/fixes/131_fix_aliased_tag_categories.rb b/script/fixes/131_fix_aliased_tag_categories.rb new file mode 100755 index 000000000..5485757a8 --- /dev/null +++ b/script/fixes/131_fix_aliased_tag_categories.rb @@ -0,0 +1,15 @@ +#!/usr/bin/env ruby + +require_relative "base" + +with_confirmation do + fix = ENV.fetch("FIX", "false").truthy? + + aliases = TagAlias.active.joins(:antecedent_tag, :consequent_tag).where("tags.category != consequent_tags_tag_aliases.category") + aliases.find_each do |tag_alias| + tag_alias.antecedent_tag.assign_attributes(category: tag_alias.consequent_tag.category, updater: User.system) + + puts ({ id: tag_alias.id, from: tag_alias.antecedent_tag.name, to: tag_alias.consequent_tag.name, changes: tag_alias.antecedent_tag.changes }).to_json + tag_alias.antecedent_tag.save!(touch: false) if fix + end +end diff --git a/test/unit/post_test.rb b/test/unit/post_test.rb index 4a64a098c..cb681a514 100644 --- a/test/unit/post_test.rb +++ b/test/unit/post_test.rb @@ -587,13 +587,13 @@ class PostTest < ActiveSupport::TestCase assert_equal(Tag.categories.character, tag.last_version.category) end - should "change the category for an aliased tag" do + should "not change the category for an aliased tag" do create(:tag_alias, antecedent_name: "hoge", consequent_name: "moge") post = create(:post, tag_string: "char:hoge") assert_equal(["moge"], post.tag_array) assert_equal(Tag.categories.general, Tag.find_by_name("moge").category) - assert_equal(Tag.categories.character, Tag.find_by_name("hoge").category) + assert_equal(Tag.categories.general, Tag.find_by_name("hoge").category) end should "not raise an exception for an invalid tag name" do diff --git a/test/unit/tag_test.rb b/test/unit/tag_test.rb index 27b421c2c..1b8b5034f 100644 --- a/test/unit/tag_test.rb +++ b/test/unit/tag_test.rb @@ -175,6 +175,17 @@ class TagTest < ActiveSupport::TestCase assert_equal(0, tag.reload.category) end + should "not change category if the tag is aliased" do + t1 = create(:tag, name: "ff7", category: Tag.categories.copyright) + t2 = create(:tag, name: "final_fantasy_vii", category: Tag.categories.copyright) + ta = create(:tag_alias, antecedent_name: "ff7", consequent_name: "final_fantasy_vii") + + t1.reload.category = Tag.categories.character + + assert_equal(false, t1.valid?) + assert_equal(["Can't change the category of an aliased tag"], t1.errors[:base]) + end + should "update post tag counts when the category is changed" do post = FactoryBot.create(:post, tag_string: "test") assert_equal(1, post.tag_count_general) @@ -186,6 +197,15 @@ class TagTest < ActiveSupport::TestCase assert_equal(1, post.tag_count_character) end + should "update aliased tags when the tag's category is changed" do + t1 = create(:tag, name: "ff7", category: Tag.categories.general) + t2 = create(:tag, name: "final_fantasy_vii", category: Tag.categories.general) + ta = create(:tag_alias, antecedent_name: "ff7", consequent_name: "final_fantasy_vii") + t2.update!(category: Tag.categories.copyright, updater: User.system) + + assert_equal("Copyright", t1.reload.category_name) + end + should "be created when one doesn't exist" do assert_difference("Tag.count", 1) do tag = Tag.find_or_create_by_name("hoge")